feat: 使用 redission 作为消息中间件
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
setArchivesBaseName("HamsterBall-Common")
|
||||
|
||||
dependencies {
|
||||
// https://mvnrepository.com/artifact/com.google.code.gson/gson
|
||||
//noinspection GradlePackageUpdate
|
||||
//noinspection VulnerableLibrariesLocal
|
||||
compileOnly 'com.google.code.gson:gson:2.8.0'
|
||||
// https://mvnrepository.com/artifact/io.netty/netty-all
|
||||
compileOnly 'io.netty:netty-all:4.1.86.Final'
|
||||
|
||||
compileOnly "cn.hamster3.mc.plugin:core-common:${hamster_core_version}"
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
}
|
25
ball-common/build.gradle.kts
Normal file
25
ball-common/build.gradle.kts
Normal file
@@ -0,0 +1,25 @@
|
||||
@file:Suppress("VulnerableLibrariesLocal")
|
||||
|
||||
dependencies {
|
||||
// https://mvnrepository.com/artifact/com.google.code.gson/gson
|
||||
compileOnly("com.google.code.gson:gson:2.8.0")
|
||||
// https://mvnrepository.com/artifact/io.netty/netty-all
|
||||
compileOnly("io.netty:netty-all:4.1.86.Final")
|
||||
|
||||
val hamsterCoreVersion = property("hamster_core_version")
|
||||
compileOnly("cn.hamster3.mc.plugin:core-common:${hamsterCoreVersion}")
|
||||
|
||||
val redissionVersion = property("redission_version")
|
||||
implementation("org.redisson:redisson:${redissionVersion}") {
|
||||
isTransitive = false
|
||||
exclude(group = "io.netty")
|
||||
exclude(group = "org.yaml")
|
||||
exclude(group = "org.slf4j")
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
withType<Jar>() {
|
||||
archiveBaseName = "HamsterBall-Common"
|
||||
}
|
||||
}
|
@@ -1,7 +1,5 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.api;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.common.config.BallConfig;
|
||||
import cn.hamster3.mc.plugin.ball.common.connector.BallChannelInitializer;
|
||||
import cn.hamster3.mc.plugin.ball.common.data.BallLocation;
|
||||
import cn.hamster3.mc.plugin.ball.common.data.BallMessageInfo;
|
||||
import cn.hamster3.mc.plugin.ball.common.entity.BallPlayerInfo;
|
||||
@@ -14,19 +12,15 @@ import cn.hamster3.mc.plugin.ball.common.event.server.ServerOfflineEvent;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.server.ServerOnlineEvent;
|
||||
import cn.hamster3.mc.plugin.ball.common.listener.BallListener;
|
||||
import cn.hamster3.mc.plugin.ball.common.listener.ListenerPriority;
|
||||
import cn.hamster3.mc.plugin.ball.common.utils.OS;
|
||||
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.net.kyori.adventure.text.Component;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.redisson.api.RTopic;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.client.codec.StringCodec;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.*;
|
||||
@@ -48,44 +42,21 @@ public abstract class BallAPI {
|
||||
protected final Map<String, BallServerInfo> serverInfo;
|
||||
@NotNull
|
||||
protected final Map<UUID, BallPlayerInfo> playerInfo;
|
||||
|
||||
@NotNull
|
||||
private final BallConfig config;
|
||||
|
||||
@NotNull
|
||||
private final Bootstrap bootstrap;
|
||||
@NotNull
|
||||
private final EventLoopGroup executors;
|
||||
protected Channel channel;
|
||||
|
||||
private boolean enabled;
|
||||
private boolean connected;
|
||||
|
||||
private final BallServerInfo localServerInfo;
|
||||
@NotNull
|
||||
private List<BallListener> listeners;
|
||||
|
||||
protected BallAPI(@NotNull BallConfig config) {
|
||||
this.config = config;
|
||||
this.enabled = false;
|
||||
this.connected = false;
|
||||
@Getter
|
||||
private boolean enabled;
|
||||
|
||||
public BallAPI(@NotNull BallServerInfo localServerInfo) {
|
||||
this.localServerInfo = localServerInfo;
|
||||
|
||||
serverInfo = new ConcurrentHashMap<>();
|
||||
playerInfo = new ConcurrentHashMap<>();
|
||||
listeners = new ArrayList<>();
|
||||
|
||||
OS currentOS = OS.getCurrentOS();
|
||||
getLogger().info(String.format(
|
||||
"当前操作系统为: %s. 选择 IO 模式为: %s",
|
||||
currentOS.name(), currentOS.getIOModeName()
|
||||
));
|
||||
|
||||
executors = currentOS.getEventLoopGroup(config.getEventLoopThread());
|
||||
bootstrap = new Bootstrap()
|
||||
.group(executors)
|
||||
.channel(currentOS.getSocketChannel())
|
||||
.option(ChannelOption.TCP_NODELAY, true)
|
||||
.handler(new BallChannelInitializer());
|
||||
|
||||
addListener(new BallListener() {
|
||||
@Override
|
||||
public ListenerPriority getPriority() {
|
||||
@@ -158,11 +129,10 @@ public abstract class BallAPI {
|
||||
@Override
|
||||
public void onConnectRefused() {
|
||||
enabled = false;
|
||||
connected = false;
|
||||
executors.shutdownGracefully();
|
||||
getLogger().info("连接至服务中心的请求被拒绝,已关闭仓鼠球。");
|
||||
}
|
||||
});
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,11 +148,8 @@ public abstract class BallAPI {
|
||||
if (enabled) {
|
||||
return;
|
||||
}
|
||||
enabled = true;
|
||||
BallServerInfo localInfo = getLocalServerInfo();
|
||||
|
||||
connect();
|
||||
|
||||
try (Connection connection = BallAPI.getInstance().getDatasource().getConnection()) {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
statement.execute("CREATE TABLE IF NOT EXISTS `hamster_ball_player_info`(" +
|
||||
@@ -246,68 +213,10 @@ public abstract class BallAPI {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void connect() throws InterruptedException {
|
||||
if (!enabled) {
|
||||
getLogger().info("仓鼠球已关闭,拒绝启动连接!");
|
||||
return;
|
||||
}
|
||||
getLogger().info("准备连接至仓鼠球服务中心!");
|
||||
ChannelFuture future = bootstrap.connect(config.getHost(), config.getPort()).await();
|
||||
if (future.isSuccess()) {
|
||||
channel = future.channel();
|
||||
connected = true;
|
||||
for (BallListener listener : listeners) {
|
||||
listener.onConnectActive();
|
||||
}
|
||||
getLogger().info("已连接至仓鼠球服务中心!");
|
||||
} else {
|
||||
getLogger().warning("连接至仓鼠球服务中心失败!");
|
||||
}
|
||||
}
|
||||
|
||||
public void reconnect(int tryCount) {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
if (channel != null && channel.isOpen() && channel.isRegistered() && channel.isActive() && channel.isWritable()) {
|
||||
return;
|
||||
}
|
||||
channel = null;
|
||||
connected = false;
|
||||
if (tryCount <= 0) {
|
||||
for (BallListener listener : getListeners()) {
|
||||
try {
|
||||
listener.onServiceDead();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
connect();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (channel != null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(3000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
reconnect(tryCount - 1);
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
protected void disable() throws SQLException, InterruptedException {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
enabled = false;
|
||||
|
||||
sendBallMessage(new BallMessageInfo(BALL_CHANNEL, ServerOfflineEvent.ACTION, new ServerOfflineEvent(getLocalServerId())), true);
|
||||
|
||||
try (Connection connection = BallAPI.getInstance().getDatasource().getConnection()) {
|
||||
@@ -325,10 +234,7 @@ public abstract class BallAPI {
|
||||
statement.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
channel = null;
|
||||
connected = false;
|
||||
executors.shutdownGracefully().await();
|
||||
getRedissonClient().shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -608,28 +514,6 @@ public abstract class BallAPI {
|
||||
sendBallMessage(new BallMessageInfo(channel, action));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送一条有附加参数的服务消息
|
||||
*
|
||||
* @param channel 消息标签
|
||||
* @param action 执行动作
|
||||
* @param content 附加参数
|
||||
*/
|
||||
public void sendBallMessage(@NotNull String channel, @NotNull String action, @NotNull String content) {
|
||||
sendBallMessage(new BallMessageInfo(channel, action, new JsonPrimitive(content)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送一条有附加参数的消息
|
||||
*
|
||||
* @param channel 消息频道
|
||||
* @param action 执行动作
|
||||
* @param content 附加参数
|
||||
*/
|
||||
public void sendBallMessage(@NotNull String channel, @NotNull String action, @NotNull JsonElement content) {
|
||||
sendBallMessage(new BallMessageInfo(channel, action, content));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送一条有附加参数的服务消息
|
||||
*
|
||||
@@ -657,17 +541,12 @@ public abstract class BallAPI {
|
||||
* @param block 是否阻塞(设置为 true 则必须等待消息写入网络的操作完成后,该方法才会退出)
|
||||
*/
|
||||
public void sendBallMessage(@NotNull BallMessageInfo messageInfo, boolean block) {
|
||||
if (channel == null || !channel.isWritable()) {
|
||||
getLogger().warning("由于服务不可用,有一条消息发送失败了: " + messageInfo);
|
||||
return;
|
||||
}
|
||||
ChannelFuture future = channel.writeAndFlush(CoreUtils.GSON.toJson(messageInfo));
|
||||
String string = CoreUtils.GSON.toJson(messageInfo);
|
||||
RTopic topic = getRedissonClient().getTopic(BallAPI.BALL_CHANNEL, StringCodec.INSTANCE);
|
||||
if (block) {
|
||||
try {
|
||||
future.await();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
topic.publish(string);
|
||||
} else {
|
||||
topic.publishAsync(string);
|
||||
}
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
@@ -678,36 +557,6 @@ public abstract class BallAPI {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<DisplayMessage> getCachedPlayerMessage(@NotNull UUID uuid) throws SQLException {
|
||||
ArrayList<DisplayMessage> list = new ArrayList<>();
|
||||
try (Connection connection = BallAPI.getInstance().getDatasource().getConnection()) {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT message FROM `hamster_ball_cached_message` WHERE `uuid`=?;"
|
||||
)) {
|
||||
statement.setString(1, uuid.toString());
|
||||
try (ResultSet set = statement.executeQuery()) {
|
||||
while (set.next()) {
|
||||
DisplayMessage msg = CoreUtils.GSON.fromJson(set.getString("message"), DisplayMessage.class);
|
||||
list.add(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public void removeCachedPlayerMessage(@NotNull UUID uuid) throws SQLException {
|
||||
try (Connection connection = BallAPI.getInstance().getDatasource().getConnection()) {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"DELETE FROM `hamster_ball_cached_message` WHERE `uuid`=?;"
|
||||
)) {
|
||||
statement.setString(1, uuid.toString());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(@NotNull BallListener listener) {
|
||||
ArrayList<BallListener> newListeners = new ArrayList<>(listeners);
|
||||
newListeners.add(listener);
|
||||
@@ -728,12 +577,12 @@ public abstract class BallAPI {
|
||||
*/
|
||||
@NotNull
|
||||
public BallServerInfo getLocalServerInfo() {
|
||||
return config.getLocalInfo();
|
||||
return localServerInfo;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getLocalServerId() {
|
||||
return config.getLocalInfo().getId();
|
||||
return localServerInfo.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -848,11 +697,6 @@ public abstract class BallAPI {
|
||||
return info.getName();
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, BallServerInfo> getAllServerInfo() {
|
||||
return serverInfo;
|
||||
@@ -873,4 +717,7 @@ public abstract class BallAPI {
|
||||
|
||||
@NotNull
|
||||
public abstract DataSource getDatasource();
|
||||
|
||||
@NotNull
|
||||
public abstract RedissonClient getRedissonClient();
|
||||
}
|
||||
|
@@ -0,0 +1,41 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.codec;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.common.data.BallMessageInfo;
|
||||
import cn.hamster3.mc.plugin.core.common.util.CoreUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import org.redisson.client.codec.BaseCodec;
|
||||
import org.redisson.client.protocol.Decoder;
|
||||
import org.redisson.client.protocol.Encoder;
|
||||
import org.redisson.codec.JsonCodec;
|
||||
|
||||
public class BallMessageInfoCodec extends BaseCodec implements JsonCodec<BallMessageInfoCodec> {
|
||||
public static final BallMessageInfoCodec INSTANCE = new BallMessageInfoCodec();
|
||||
|
||||
private final Encoder encoder = in -> {
|
||||
ByteBuf out = ByteBufAllocator.DEFAULT.buffer();
|
||||
out.writeCharSequence(in.toString(), CharsetUtil.UTF_8);
|
||||
return out;
|
||||
};
|
||||
|
||||
private final Decoder<Object> decoder = (buf, state) -> {
|
||||
String str = buf.toString(CharsetUtil.UTF_8);
|
||||
buf.readerIndex(buf.readableBytes());
|
||||
return CoreUtils.GSON.fromJson(str, BallMessageInfo.class);
|
||||
};
|
||||
|
||||
private BallMessageInfoCodec() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Decoder<Object> getValueDecoder() {
|
||||
return decoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Encoder getValueEncoder() {
|
||||
return encoder;
|
||||
}
|
||||
|
||||
}
|
@@ -1,195 +0,0 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.connector;
|
||||
|
||||
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
|
||||
import cn.hamster3.mc.plugin.ball.common.data.BallMessageInfo;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.player.*;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.server.ServerOfflineEvent;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.server.ServerOnlineEvent;
|
||||
import cn.hamster3.mc.plugin.ball.common.listener.BallListener;
|
||||
import cn.hamster3.mc.plugin.core.common.util.CoreUtils;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
public class BallChannelHandler extends SimpleChannelInboundHandler<String> {
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext context, String message) {
|
||||
if ("pong".equals(message)) {
|
||||
return;
|
||||
}
|
||||
if ("connection refused".equals(message)) {
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
listener.onConnectRefused();
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
BallMessageInfo info = CoreUtils.GSON.fromJson(message, BallMessageInfo.class);
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
listener.onMessageReceived(info);
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (!BallAPI.BALL_CHANNEL.equals(info.getChannel())) {
|
||||
return;
|
||||
}
|
||||
switch (info.getAction()) {
|
||||
case BallPlayerPreLoginEvent.ACTION: {
|
||||
BallPlayerPreLoginEvent event = CoreUtils.GSON.fromJson(info.getContent(), BallPlayerPreLoginEvent.class);
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
listener.onBallPlayerPreLogin(event);
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BallPlayerLoginEvent.ACTION: {
|
||||
BallPlayerLoginEvent event = CoreUtils.GSON.fromJson(info.getContent(), BallPlayerLoginEvent.class);
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
listener.onBallPlayerLogin(event);
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BallPlayerPostLoginEvent.ACTION: {
|
||||
BallPlayerPostLoginEvent event = CoreUtils.GSON.fromJson(info.getContent(), BallPlayerPostLoginEvent.class);
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
listener.onBallPlayerPostLogin(event);
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BallPlayerPreConnectServerEvent.ACTION: {
|
||||
BallPlayerPreConnectServerEvent event = CoreUtils.GSON.fromJson(info.getContent(), BallPlayerPreConnectServerEvent.class);
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
listener.onBallPlayerPreConnectServer(event);
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BallPlayerConnectServerEvent.ACTION: {
|
||||
BallPlayerConnectServerEvent event = CoreUtils.GSON.fromJson(info.getContent(), BallPlayerConnectServerEvent.class);
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
listener.onBallPlayerConnectServer(event);
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BallPlayerPostConnectServerEvent.ACTION: {
|
||||
BallPlayerPostConnectServerEvent event = CoreUtils.GSON.fromJson(info.getContent(), BallPlayerPostConnectServerEvent.class);
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
listener.onBallPlayerPostConnectServer(event);
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BallPlayerLogoutEvent.ACTION: {
|
||||
BallPlayerLogoutEvent event = CoreUtils.GSON.fromJson(info.getContent(), BallPlayerLogoutEvent.class);
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
listener.onBallPlayerLogout(event);
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BallPlayerInfoUpdateEvent.ACTION: {
|
||||
BallPlayerInfoUpdateEvent event = CoreUtils.GSON.fromJson(info.getContent(), BallPlayerInfoUpdateEvent.class);
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
listener.onBallPlayerInfoUpdate(event);
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BallPlayerChatEvent.ACTION: {
|
||||
BallPlayerChatEvent event = CoreUtils.GSON.fromJson(info.getContent(), BallPlayerChatEvent.class);
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
listener.onBallPlayerChat(event);
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ServerOfflineEvent.ACTION: {
|
||||
ServerOfflineEvent event = CoreUtils.GSON.fromJson(info.getContent(), ServerOfflineEvent.class);
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
listener.onServerOffline(event);
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ServerOnlineEvent.ACTION: {
|
||||
ServerOnlineEvent event = CoreUtils.GSON.fromJson(info.getContent(), ServerOnlineEvent.class);
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
listener.onServerOnline(event);
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(@NotNull ChannelHandlerContext context) {
|
||||
BallAPI.getInstance().getLogger().info("与服务器 " + context.channel().remoteAddress() + " 建立了连接.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(@NotNull ChannelHandlerContext context) {
|
||||
context.close();
|
||||
BallAPI.getInstance().getLogger().warning("与服务器 " + context.channel().remoteAddress() + " 的连接已断开.");
|
||||
CoreUtils.WORKER_EXECUTOR.submit(() -> BallAPI.getInstance().reconnect(5));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
|
||||
SocketAddress address = context.channel().remoteAddress();
|
||||
BallAPI.getInstance().getLogger().log(Level.WARNING, "与服务器 " + address + " 通信时出现了一个错误: ", cause);
|
||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||
try {
|
||||
listener.onConnectException(cause);
|
||||
} catch (Exception | Error e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.connector;
|
||||
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
|
||||
import io.netty.handler.codec.LengthFieldPrepender;
|
||||
import io.netty.handler.codec.string.StringDecoder;
|
||||
import io.netty.handler.codec.string.StringEncoder;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BallChannelInitializer extends ChannelInitializer<SocketChannel> {
|
||||
@Override
|
||||
protected void initChannel(@NotNull SocketChannel channel) {
|
||||
channel.pipeline()
|
||||
.addLast(new IdleStateHandler(0, 5, 0, TimeUnit.SECONDS))
|
||||
.addLast(new BallKeepAliveHandler())
|
||||
.addLast(new LengthFieldPrepender(8))
|
||||
.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 8, 0, 8))
|
||||
.addLast(new StringDecoder(StandardCharsets.UTF_8))
|
||||
.addLast(new StringEncoder(StandardCharsets.UTF_8))
|
||||
.addLast(new BallChannelHandler());
|
||||
}
|
||||
}
|
||||
|
@@ -1,18 +0,0 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.connector;
|
||||
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
public class BallKeepAliveHandler extends ChannelInboundHandlerAdapter {
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext context, Object event) throws Exception {
|
||||
if (event instanceof IdleStateEvent) {
|
||||
context.channel().writeAndFlush("ping");
|
||||
return;
|
||||
}
|
||||
super.userEventTriggered(context, event);
|
||||
}
|
||||
}
|
@@ -6,7 +6,6 @@ import cn.hamster3.mc.plugin.core.common.util.CoreUtils;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import lombok.Data;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -69,21 +68,7 @@ public class BallMessageInfo {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public BallMessageInfo(@NotNull String channel, @NotNull String action, String content) {
|
||||
senderID = BallAPI.getInstance().getLocalServerId();
|
||||
this.channel = channel;
|
||||
this.action = action;
|
||||
this.content = new JsonPrimitive(content);
|
||||
}
|
||||
|
||||
public BallMessageInfo(@NotNull String channel, @NotNull String action, JsonElement content) {
|
||||
this.channel = channel;
|
||||
senderID = BallAPI.getInstance().getLocalServerId();
|
||||
this.action = action;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public BallMessageInfo(@NotNull String channel, @NotNull String action, Object content) {
|
||||
public BallMessageInfo(@NotNull String channel, @NotNull String action, @NotNull Object content) {
|
||||
this.channel = channel;
|
||||
senderID = BallAPI.getInstance().getLocalServerId();
|
||||
this.action = action;
|
||||
|
@@ -2,108 +2,16 @@ 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.BallMessageInfo;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.player.*;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.server.ServerOfflineEvent;
|
||||
import cn.hamster3.mc.plugin.ball.common.event.server.ServerOnlineEvent;
|
||||
import cn.hamster3.mc.plugin.core.lib.net.kyori.adventure.text.serializer.json.JSONComponentSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.redisson.api.listener.MessageListener;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public final class BallDebugListener implements BallListener {
|
||||
public class BallDebugListener implements MessageListener<BallMessageInfo> {
|
||||
public static final BallDebugListener INSTANCE = new BallDebugListener();
|
||||
|
||||
private BallDebugListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenerPriority getPriority() {
|
||||
return ListenerPriority.MONITOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectActive() {
|
||||
BallAPI.getInstance().getLogger().info("连接已可用。");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectException(Throwable throwable) {
|
||||
BallAPI.getInstance().getLogger().log(Level.INFO, "连接出现错误!", throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDead() {
|
||||
BallAPI.getInstance().getLogger().info("已无法建立与服务中心的连接!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBallPlayerPreLogin(@NotNull BallPlayerPreLoginEvent event) {
|
||||
BallAPI.getInstance().getLogger().info("BallPlayerPreLoginEvent: " + event.getPlayerName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBallPlayerLogin(@NotNull BallPlayerLoginEvent event) {
|
||||
BallAPI.getInstance().getLogger().info("BallPlayerLoginEvent: " + event.getPlayerInfo().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBallPlayerPostLogin(@NotNull BallPlayerPostLoginEvent event) {
|
||||
BallAPI.getInstance().getLogger().info("BallPlayerPostLoginEvent: " + event.getPlayerInfo().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBallPlayerPreConnectServer(@NotNull BallPlayerPreConnectServerEvent event) {
|
||||
BallAPI.getInstance().getLogger().info("BallPlayerPreConnectServerEvent: ");
|
||||
BallAPI.getInstance().getLogger().info("player: " + event.getPlayerInfo().getName());
|
||||
BallAPI.getInstance().getLogger().info("from: " + event.getFrom());
|
||||
BallAPI.getInstance().getLogger().info("to: " + event.getTo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBallPlayerConnectServer(@NotNull BallPlayerConnectServerEvent event) {
|
||||
BallAPI.getInstance().getLogger().info("BallPlayerConnectServerEvent: ");
|
||||
BallAPI.getInstance().getLogger().info("player: " + event.getPlayerInfo().getName());
|
||||
BallAPI.getInstance().getLogger().info("from: " + event.getFrom());
|
||||
BallAPI.getInstance().getLogger().info("to: " + event.getTo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBallPlayerPostConnectServer(@NotNull BallPlayerPostConnectServerEvent event) {
|
||||
BallAPI.getInstance().getLogger().info("BallPlayerPostConnectServerEvent: ");
|
||||
BallAPI.getInstance().getLogger().info("player: " + event.getPlayerInfo().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBallPlayerLogout(@NotNull BallPlayerLogoutEvent event) {
|
||||
BallAPI.getInstance().getLogger().info("BallPlayerLogoutEvent: ");
|
||||
BallAPI.getInstance().getLogger().info("player: " + event.getPlayerInfo().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBallPlayerChat(@NotNull BallPlayerChatEvent event) {
|
||||
BallAPI.getInstance().getLogger().info("BallPlayerChatEvent: ");
|
||||
BallAPI.getInstance().getLogger().info("displayName: " + event.getDisplayName());
|
||||
BallAPI.getInstance().getLogger().info("playerUUID: " + event.getPlayerUUID());
|
||||
BallAPI.getInstance().getLogger().info("message: " + JSONComponentSerializer.json().serialize(event.getMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServerOffline(@NotNull ServerOfflineEvent event) {
|
||||
BallAPI.getInstance().getLogger().info("服务器已离线: " + event.getServerID());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServerOnline(@NotNull ServerOnlineEvent event) {
|
||||
BallAPI.getInstance().getLogger().info("服务器已上线: " + event.getServerInfo().getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageSend(@NotNull BallMessageInfo event) {
|
||||
BallAPI.getInstance().getLogger().info("发送了一条消息: " + event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(@NotNull BallMessageInfo event) {
|
||||
public void onMessage(CharSequence channel, BallMessageInfo event) {
|
||||
BallAPI.getInstance().getLogger().info("收到了一条消息: " + event);
|
||||
}
|
||||
}
|
||||
|
@@ -1,89 +0,0 @@
|
||||
package cn.hamster3.mc.plugin.ball.common.utils;
|
||||
|
||||
import cn.hamster3.mc.plugin.core.common.thread.NamedThreadFactory;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.ServerChannel;
|
||||
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollServerSocketChannel;
|
||||
import io.netty.channel.epoll.EpollSocketChannel;
|
||||
import io.netty.channel.kqueue.KQueueEventLoopGroup;
|
||||
import io.netty.channel.kqueue.KQueueServerSocketChannel;
|
||||
import io.netty.channel.kqueue.KQueueSocketChannel;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
public enum OS {
|
||||
WINDOWS,
|
||||
LINUX,
|
||||
MACOS,
|
||||
OTHER;
|
||||
|
||||
private static final ThreadFactory THREAD_FACTORY = new NamedThreadFactory("HamsterBall-IO");
|
||||
|
||||
@NotNull
|
||||
public static OS getCurrentOS() {
|
||||
String s = System.getProperties().get("os.name").toString().toLowerCase();
|
||||
if (s.contains("windows")) {
|
||||
return WINDOWS;
|
||||
}
|
||||
if (s.contains("linux")) {
|
||||
return LINUX;
|
||||
}
|
||||
if (s.contains("mac")) {
|
||||
return MACOS;
|
||||
}
|
||||
return OTHER;
|
||||
}
|
||||
|
||||
public String getIOModeName() {
|
||||
switch (this) {
|
||||
case LINUX:
|
||||
return "epoll";
|
||||
case MACOS:
|
||||
return "kqueue";
|
||||
default:
|
||||
return "nio";
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public EventLoopGroup getEventLoopGroup(int nThread) {
|
||||
switch (this) {
|
||||
case LINUX:
|
||||
return new EpollEventLoopGroup(nThread, THREAD_FACTORY);
|
||||
case MACOS:
|
||||
return new KQueueEventLoopGroup(nThread, THREAD_FACTORY);
|
||||
default:
|
||||
return new NioEventLoopGroup(nThread, THREAD_FACTORY);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Class<? extends Channel> getSocketChannel() {
|
||||
switch (this) {
|
||||
case LINUX:
|
||||
return EpollSocketChannel.class;
|
||||
case MACOS:
|
||||
return KQueueSocketChannel.class;
|
||||
default:
|
||||
return NioSocketChannel.class;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Class<? extends ServerChannel> getServerSocketChannel() {
|
||||
switch (this) {
|
||||
case LINUX:
|
||||
return EpollServerSocketChannel.class;
|
||||
case MACOS:
|
||||
return KQueueServerSocketChannel.class;
|
||||
default:
|
||||
return NioServerSocketChannel.class;
|
||||
}
|
||||
}
|
||||
}
|
36
ball-common/src/main/resources/redission.yml
Normal file
36
ball-common/src/main/resources/redission.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
singleServerConfig:
|
||||
address: "redis://localhost:6379"
|
||||
password: "Reids123.."
|
||||
username: "default"
|
||||
database: 1
|
||||
clientName: "HamsterBall"
|
||||
idleConnectionTimeout: 10000
|
||||
connectTimeout: 10000
|
||||
timeout: 3000
|
||||
retryAttempts: 3
|
||||
retryInterval: 1500
|
||||
subscriptionsPerConnection: 5
|
||||
sslEnableEndpointIdentification: true
|
||||
sslProvider: "JDK"
|
||||
pingConnectionInterval: 30000
|
||||
keepAlive: false
|
||||
tcpNoDelay: true
|
||||
subscriptionConnectionMinimumIdleSize: 1
|
||||
subscriptionConnectionPoolSize: 50
|
||||
connectionMinimumIdleSize: 1
|
||||
connectionPoolSize: 10
|
||||
dnsMonitoringInterval: 5000
|
||||
threads: 4
|
||||
nettyThreads: 4
|
||||
referenceEnabled: true
|
||||
lockWatchdogTimeout: 30000
|
||||
checkLockSyncedSlaves: true
|
||||
slavesSyncTimeout: 1000
|
||||
reliableTopicWatchdogTimeout: 600000
|
||||
keepPubSubOrder: true
|
||||
useScriptCache: false
|
||||
minCleanUpDelay: 5
|
||||
maxCleanUpDelay: 1800
|
||||
cleanUpKeysAmount: 100
|
||||
useThreadClassLoader: true
|
||||
lazyInitialization: false
|
Reference in New Issue
Block a user