feat: 使用 redission 作为消息中间件
This commit is contained in:
@@ -1,41 +0,0 @@
|
|||||||
setArchivesBaseName("HamsterBall-Bukkit")
|
|
||||||
|
|
||||||
evaluationDependsOn(':ball-common')
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
apiShade project(":ball-common") transitive false
|
|
||||||
|
|
||||||
//noinspection VulnerableLibrariesLocal
|
|
||||||
compileOnly 'org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT'
|
|
||||||
|
|
||||||
compileOnly "cn.hamster3.mc.plugin:core-bukkit:${hamster_core_version}"
|
|
||||||
compileOnly "me.clip:placeholderapi:2.11.2" transitive false
|
|
||||||
}
|
|
||||||
|
|
||||||
processResources {
|
|
||||||
filesMatching("plugin.yml") {
|
|
||||||
expand(project.properties)
|
|
||||||
}
|
|
||||||
duplicatesStrategy(DuplicatesStrategy.EXCLUDE)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.compileJava.dependsOn(":ball-common:build")
|
|
||||||
tasks.register("shadowJar", Jar) {
|
|
||||||
dependsOn("jar")
|
|
||||||
from([
|
|
||||||
tasks.jar.outputs.files.collect {
|
|
||||||
it.isDirectory() ? it : zipTree(it)
|
|
||||||
},
|
|
||||||
configurations.shade.collect {
|
|
||||||
it.isDirectory() ? it : zipTree(it)
|
|
||||||
},
|
|
||||||
configurations.apiShade.collect {
|
|
||||||
it.isDirectory() ? it : zipTree(it)
|
|
||||||
},
|
|
||||||
configurations.implementationShade.collect {
|
|
||||||
it.isDirectory() ? it : zipTree(it)
|
|
||||||
}
|
|
||||||
])
|
|
||||||
destinationDirectory = rootProject.buildDir
|
|
||||||
}
|
|
||||||
tasks.build.dependsOn(shadowJar)
|
|
30
ball-bukkit/build.gradle.kts
Normal file
30
ball-bukkit/build.gradle.kts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
evaluationDependsOn(":ball-common")
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":ball-common")) { isTransitive = false }
|
||||||
|
|
||||||
|
compileOnly("org.spigotmc:spigot-api:1.20.1-R0.1-SNAPSHOT")
|
||||||
|
|
||||||
|
val hamsterCoreVersion = property("hamster_core_version")
|
||||||
|
compileOnly("cn.hamster3.mc.plugin:core-bukkit:${hamsterCoreVersion}")
|
||||||
|
compileOnly("me.clip:placeholderapi:2.11.2") { isTransitive = false }
|
||||||
|
|
||||||
|
val redissionVersion = property("redission_version")
|
||||||
|
implementation("org.redisson:redisson:${redissionVersion}") {
|
||||||
|
exclude(group = "io.netty")
|
||||||
|
exclude(group = "org.yaml")
|
||||||
|
exclude(group = "org.slf4j")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
processResources {
|
||||||
|
filesMatching("plugin.yml") {
|
||||||
|
expand(project.properties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
withType<Jar>() {
|
||||||
|
archiveBaseName = "HamsterBall-Bukkit"
|
||||||
|
}
|
||||||
|
}
|
@@ -30,9 +30,9 @@ public class HamsterBallPlugin extends JavaPlugin {
|
|||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
instance = this;
|
instance = this;
|
||||||
Logger logger = getLogger();
|
Logger logger = getLogger();
|
||||||
BallBukkitAPI.init();
|
|
||||||
logger.info("BallBukkitAPI 已初始化.");
|
|
||||||
try {
|
try {
|
||||||
|
BallBukkitAPI.init();
|
||||||
|
logger.info("BallBukkitAPI 已初始化.");
|
||||||
BallBukkitAPI.getInstance().enable();
|
BallBukkitAPI.getInstance().enable();
|
||||||
logger.info("BallBukkitAPI 已启动.");
|
logger.info("BallBukkitAPI 已启动.");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -43,7 +43,7 @@ public class HamsterBallPlugin extends JavaPlugin {
|
|||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
Logger logger = getLogger();
|
Logger logger = getLogger();
|
||||||
if (!BallAPI.getInstance().isConnected()) {
|
if (!BallAPI.getInstance().isEnabled()) {
|
||||||
sync(() -> {
|
sync(() -> {
|
||||||
logger.info("由于 HamsterBall 未能成功连接, 服务器将立即关闭.");
|
logger.info("由于 HamsterBall 未能成功连接, 服务器将立即关闭.");
|
||||||
Bukkit.shutdown();
|
Bukkit.shutdown();
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
package cn.hamster3.mc.plugin.ball.bukkit.api;
|
package cn.hamster3.mc.plugin.ball.bukkit.api;
|
||||||
|
|
||||||
import cn.hamster3.mc.plugin.ball.bukkit.HamsterBallPlugin;
|
import cn.hamster3.mc.plugin.ball.bukkit.HamsterBallPlugin;
|
||||||
import cn.hamster3.mc.plugin.ball.bukkit.listener.BallBukkitListener;
|
|
||||||
import cn.hamster3.mc.plugin.ball.bukkit.util.BallBukkitUtils;
|
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.api.BallAPI;
|
||||||
import cn.hamster3.mc.plugin.ball.common.config.BallConfig;
|
import cn.hamster3.mc.plugin.ball.common.codec.BallMessageInfoCodec;
|
||||||
|
import cn.hamster3.mc.plugin.ball.common.data.BallMessageInfo;
|
||||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo;
|
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.entity.BallServerType;
|
||||||
import cn.hamster3.mc.plugin.ball.common.listener.BallDebugListener;
|
import cn.hamster3.mc.plugin.ball.common.listener.BallDebugListener;
|
||||||
@@ -12,63 +12,74 @@ import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
|
|||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.configuration.file.FileConfiguration;
|
import org.bukkit.configuration.file.FileConfiguration;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.redisson.Redisson;
|
||||||
|
import org.redisson.api.RTopic;
|
||||||
|
import org.redisson.api.RedissonClient;
|
||||||
|
import org.redisson.config.Config;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class BallBukkitAPI extends BallAPI {
|
public class BallBukkitAPI extends BallAPI {
|
||||||
private DataSource datasource;
|
@Nullable
|
||||||
|
private final DataSource datasource;
|
||||||
|
@NotNull
|
||||||
|
private final RedissonClient redissonClient;
|
||||||
|
|
||||||
public BallBukkitAPI(@NotNull BallConfig config) {
|
public BallBukkitAPI(@NotNull BallServerInfo localServerInfo, @Nullable DataSource datasource, @NotNull RedissonClient redissonClient) {
|
||||||
super(config);
|
super(localServerInfo);
|
||||||
|
this.datasource = datasource;
|
||||||
|
this.redissonClient = redissonClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BallBukkitAPI getInstance() {
|
public static BallBukkitAPI getInstance() {
|
||||||
return (BallBukkitAPI) instance;
|
return (BallBukkitAPI) instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void init() {
|
public static void init() throws IOException {
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
HamsterBallPlugin plugin = HamsterBallPlugin.getInstance();
|
HamsterBallPlugin plugin = HamsterBallPlugin.getInstance();
|
||||||
plugin.saveDefaultConfig();
|
plugin.saveDefaultConfig();
|
||||||
|
plugin.reloadConfig();
|
||||||
|
|
||||||
FileConfiguration pluginConfig = plugin.getConfig();
|
FileConfiguration config = plugin.getConfig();
|
||||||
Map<String, String> env = System.getenv();
|
Map<String, String> env = System.getenv();
|
||||||
|
|
||||||
String serverInfoID = env.getOrDefault("BALL_LOCAL_SERVER_INFO_ID",
|
|
||||||
pluginConfig.getString("server-info.id"));
|
|
||||||
String serverInfoName = env.getOrDefault("BALL_LOCAL_SERVER_INFO_NAME",
|
|
||||||
pluginConfig.getString("server-info.name"));
|
|
||||||
String serverInfoHost = env.getOrDefault("BALL_LOCAL_SERVER_IP",
|
|
||||||
pluginConfig.getString("server-info.host", Bukkit.getIp()));
|
|
||||||
int serverInfoPort = Integer.parseInt(env.getOrDefault("BALL_LOCAL_SERVER_PORT",
|
|
||||||
String.valueOf(pluginConfig.getInt("server-info.port", Bukkit.getPort()))));
|
|
||||||
BallServerInfo serverInfo = new BallServerInfo(
|
BallServerInfo serverInfo = new BallServerInfo(
|
||||||
serverInfoID,
|
env.getOrDefault("BALL_LOCAL_SERVER_INFO_ID", config.getString("server-info.id")),
|
||||||
serverInfoName,
|
env.getOrDefault("BALL_LOCAL_SERVER_INFO_NAME", config.getString("server-info.name")),
|
||||||
BallServerType.GAME,
|
BallServerType.GAME,
|
||||||
serverInfoHost.isEmpty() ? "127.0.0.1" : serverInfoHost,
|
env.getOrDefault("BALL_LOCAL_SERVER_IP", config.getString("server-info.host", Bukkit.getIp())),
|
||||||
serverInfoPort
|
Integer.parseInt(
|
||||||
|
env.getOrDefault("BALL_LOCAL_SERVER_PORT", String.valueOf(config.getInt("server-info.port", Bukkit.getPort())))
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
String serverHost = env.getOrDefault("BALL_SERVER_HOST",
|
DataSource datasource = BallBukkitUtils.getDataSource(config.getConfigurationSection("datasource"));
|
||||||
pluginConfig.getString("ball-server.host", "ball.hamster3.cn"));
|
File redissionConfig = new File(plugin.getDataFolder(), "redission.yml");
|
||||||
int serverPort = Integer.parseInt(env.getOrDefault("BALL_SERVER_PORT",
|
if (!redissionConfig.exists()) {
|
||||||
String.valueOf(pluginConfig.getInt("ball-server.port", 58888))));
|
Files.copy(
|
||||||
int eventLoopThread = Integer.parseInt(env.getOrDefault("BALL_EVENT_LOOP_THREAD",
|
Objects.requireNonNull(plugin.getResource("redission.yml")),
|
||||||
String.valueOf(pluginConfig.getInt("ball-server.event-loop-thread", 2))));
|
redissionConfig.toPath(),
|
||||||
BallConfig config = new BallConfig(serverInfo, serverHost, serverPort, eventLoopThread);
|
StandardCopyOption.REPLACE_EXISTING
|
||||||
|
);
|
||||||
|
}
|
||||||
|
RedissonClient redissonClient = Redisson.create(Config.fromYAML(redissionConfig));
|
||||||
|
BallBukkitAPI apiInstance = new BallBukkitAPI(serverInfo, datasource, redissonClient);
|
||||||
|
|
||||||
BallBukkitAPI apiInstance = new BallBukkitAPI(config);
|
RTopic topic = redissonClient.getTopic(BALL_CHANNEL, BallMessageInfoCodec.INSTANCE);
|
||||||
apiInstance.datasource = BallBukkitUtils.getDataSource(pluginConfig.getConfigurationSection("datasource"));
|
if (config.getBoolean("debug", false)) {
|
||||||
|
topic.addListener(BallMessageInfo.class, BallDebugListener.INSTANCE);
|
||||||
apiInstance.addListener(BallBukkitListener.INSTANCE);
|
|
||||||
if (pluginConfig.getBoolean("debug", false)) {
|
|
||||||
apiInstance.addListener(BallDebugListener.INSTANCE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instance = apiInstance;
|
instance = apiInstance;
|
||||||
@@ -93,4 +104,9 @@ public class BallBukkitAPI extends BallAPI {
|
|||||||
public @NotNull DataSource getDatasource() {
|
public @NotNull DataSource getDatasource() {
|
||||||
return datasource == null ? CoreAPI.getInstance().getDataSource() : datasource;
|
return datasource == null ? CoreAPI.getInstance().getDataSource() : datasource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull RedissonClient getRedissonClient() {
|
||||||
|
return redissonClient;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,6 @@ import cn.hamster3.mc.plugin.core.bukkit.api.CoreBukkitAPI;
|
|||||||
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.data.DisplayMessage;
|
import cn.hamster3.mc.plugin.core.common.data.DisplayMessage;
|
||||||
import cn.hamster3.mc.plugin.core.common.util.CoreUtils;
|
import cn.hamster3.mc.plugin.core.common.util.CoreUtils;
|
||||||
import cn.hamster3.mc.plugin.core.common.util.Pair;
|
|
||||||
import cn.hamster3.mc.plugin.core.lib.net.kyori.adventure.audience.Audience;
|
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 cn.hamster3.mc.plugin.core.lib.net.kyori.adventure.text.TextReplacementConfig;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
@@ -26,7 +25,11 @@ import org.bukkit.event.player.PlayerJoinEvent;
|
|||||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@@ -34,8 +37,8 @@ import java.util.UUID;
|
|||||||
public class BallBukkitListener implements Listener, BallListener {
|
public class BallBukkitListener implements Listener, BallListener {
|
||||||
public static final BallBukkitListener INSTANCE = new BallBukkitListener();
|
public static final BallBukkitListener INSTANCE = new BallBukkitListener();
|
||||||
|
|
||||||
private final HashMap<UUID, Pair<Location, DisplayMessage>> playerToLocation = new HashMap<>();
|
private final HashMap<UUID, ToLocation> playerToLocation = new HashMap<>();
|
||||||
private final HashMap<UUID, ToPlayerData> playerToPlayer = new HashMap<>();
|
private final HashMap<UUID, ToPlayer> playerToPlayer = new HashMap<>();
|
||||||
|
|
||||||
private BallBukkitListener() {
|
private BallBukkitListener() {
|
||||||
}
|
}
|
||||||
@@ -99,7 +102,7 @@ public class BallBukkitListener implements Listener, BallListener {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
playerToLocation.put(uuid, new Pair<>(location.toBukkitLocation(), event.getDoneMessage()));
|
playerToLocation.put(uuid, new ToLocation(location.toBukkitLocation(), event.getDoneMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,7 +136,7 @@ public class BallBukkitListener implements Listener, BallListener {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
ToPlayerData data = new ToPlayerData(
|
ToPlayer data = new ToPlayer(
|
||||||
toPlayer.getUniqueId(),
|
toPlayer.getUniqueId(),
|
||||||
toPlayer.getLocation(),
|
toPlayer.getLocation(),
|
||||||
event.getDoneMessage(),
|
event.getDoneMessage(),
|
||||||
@@ -151,15 +154,16 @@ public class BallBukkitListener implements Listener, BallListener {
|
|||||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
UUID uuid = player.getUniqueId();
|
UUID uuid = player.getUniqueId();
|
||||||
Pair<Location, DisplayMessage> pair = playerToLocation.remove(uuid);
|
ToLocation toLocationData = playerToLocation.remove(uuid);
|
||||||
if (pair != null) {
|
if (toLocationData != null) {
|
||||||
player.teleport(pair.getKey(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
player.teleport(toLocationData.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||||
if (pair.getValue() != null) {
|
DisplayMessage doneMessage = toLocationData.getDoneMessage();
|
||||||
|
if (doneMessage != null) {
|
||||||
Audience audience = CoreBukkitAPI.getInstance().getAudienceProvider().player(player);
|
Audience audience = CoreBukkitAPI.getInstance().getAudienceProvider().player(player);
|
||||||
pair.getValue().show(audience);
|
doneMessage.show(audience);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToPlayerData toPlayerData = playerToPlayer.remove(uuid);
|
ToPlayer toPlayerData = playerToPlayer.remove(uuid);
|
||||||
if (toPlayerData != null) {
|
if (toPlayerData != null) {
|
||||||
player.teleport(toPlayerData.location);
|
player.teleport(toPlayerData.location);
|
||||||
if (toPlayerData.doneMessage != null) {
|
if (toPlayerData.doneMessage != null) {
|
||||||
@@ -179,21 +183,58 @@ public class BallBukkitListener implements Listener, BallListener {
|
|||||||
}
|
}
|
||||||
HamsterBallPlugin.async(() -> {
|
HamsterBallPlugin.async(() -> {
|
||||||
try {
|
try {
|
||||||
List<DisplayMessage> cachedPlayerMessage = BallAPI.getInstance().getCachedPlayerMessage(uuid);
|
List<DisplayMessage> cachedPlayerMessage = getCachedPlayerMessage(uuid);
|
||||||
Audience audience = CoreAPI.getInstance().getAudienceProvider().player(uuid);
|
Audience audience = CoreAPI.getInstance().getAudienceProvider().player(uuid);
|
||||||
for (DisplayMessage message : cachedPlayerMessage) {
|
for (DisplayMessage message : cachedPlayerMessage) {
|
||||||
message.show(audience);
|
message.show(audience);
|
||||||
}
|
}
|
||||||
BallAPI.getInstance().removeCachedPlayerMessage(uuid);
|
removeCachedPlayerMessage(uuid);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
private static class ToPlayerData {
|
private static class ToLocation {
|
||||||
|
private Location location;
|
||||||
|
private DisplayMessage doneMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
private static class ToPlayer {
|
||||||
private UUID toPlayerUUID;
|
private UUID toPlayerUUID;
|
||||||
private Location location;
|
private Location location;
|
||||||
private DisplayMessage doneMessage;
|
private DisplayMessage doneMessage;
|
||||||
|
@@ -1,11 +1,6 @@
|
|||||||
# 是否允许在控制台输出调试信息
|
# 是否允许在控制台输出调试信息
|
||||||
debug: false
|
debug: false
|
||||||
|
|
||||||
ball-server:
|
|
||||||
host: "ball.hamster3.cn"
|
|
||||||
port: 58888
|
|
||||||
event-loop-thread: 2
|
|
||||||
|
|
||||||
server-info:
|
server-info:
|
||||||
# 服务器唯一识别码,最长 32 字符
|
# 服务器唯一识别码,最长 32 字符
|
||||||
# 推荐格式:全小写英文+横杠+数字尾号
|
# 推荐格式:全小写英文+横杠+数字尾号
|
||||||
@@ -19,8 +14,6 @@ server-info:
|
|||||||
# 当前子服的内网地址
|
# 当前子服的内网地址
|
||||||
# 不填则自动获取 server.properties 文件中的设置
|
# 不填则自动获取 server.properties 文件中的设置
|
||||||
# 若都为空,则自动设定为 127.0.0.1
|
# 若都为空,则自动设定为 127.0.0.1
|
||||||
# BC 端的插件会自动将这个服务器的地址和端口加入到列表
|
|
||||||
# 因此只需在这里配置好,以后新增子服时无需每次都手动更改 BC 的配置文件
|
|
||||||
# host: "127.0.0.1"
|
# host: "127.0.0.1"
|
||||||
# 当前子服的内网端口
|
# 当前子服的内网端口
|
||||||
# 不填则自动获取 server.properties 文件中的设置
|
# 不填则自动获取 server.properties 文件中的设置
|
||||||
|
@@ -1,40 +0,0 @@
|
|||||||
setArchivesBaseName("HamsterBall-BungeeCord")
|
|
||||||
|
|
||||||
evaluationDependsOn(':ball-common')
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
apiShade project(":ball-common") transitive false
|
|
||||||
|
|
||||||
//noinspection VulnerableLibrariesLocal
|
|
||||||
compileOnly 'net.md-5:bungeecord-api:1.20-R0.1-SNAPSHOT' exclude group: 'io.netty'
|
|
||||||
|
|
||||||
compileOnly "cn.hamster3.mc.plugin:core-bungeecord:${hamster_core_version}"
|
|
||||||
}
|
|
||||||
|
|
||||||
processResources {
|
|
||||||
filesMatching("bungee.yml") {
|
|
||||||
expand(project.properties)
|
|
||||||
}
|
|
||||||
duplicatesStrategy(DuplicatesStrategy.EXCLUDE)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.compileJava.dependsOn(":ball-common:build")
|
|
||||||
tasks.register("shadowJar", Jar) {
|
|
||||||
dependsOn("jar")
|
|
||||||
from([
|
|
||||||
tasks.jar.outputs.files.collect {
|
|
||||||
it.isDirectory() ? it : zipTree(it)
|
|
||||||
},
|
|
||||||
configurations.shade.collect {
|
|
||||||
it.isDirectory() ? it : zipTree(it)
|
|
||||||
},
|
|
||||||
configurations.apiShade.collect {
|
|
||||||
it.isDirectory() ? it : zipTree(it)
|
|
||||||
},
|
|
||||||
configurations.implementationShade.collect {
|
|
||||||
it.isDirectory() ? it : zipTree(it)
|
|
||||||
}
|
|
||||||
])
|
|
||||||
destinationDirectory = rootProject.buildDir
|
|
||||||
}
|
|
||||||
tasks.build.dependsOn(shadowJar)
|
|
30
ball-bungeecord/build.gradle.kts
Normal file
30
ball-bungeecord/build.gradle.kts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
evaluationDependsOn(":ball-common")
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":ball-common")) { isTransitive = false }
|
||||||
|
|
||||||
|
compileOnly("net.md-5:bungeecord-api:1.20-R0.1-SNAPSHOT")
|
||||||
|
|
||||||
|
val hamsterCoreVersion = property("hamster_core_version")
|
||||||
|
compileOnly("cn.hamster3.mc.plugin:core-bungeecord:${hamsterCoreVersion}")
|
||||||
|
compileOnly("me.clip:placeholderapi:2.11.2") { isTransitive = false }
|
||||||
|
|
||||||
|
val redissionVersion = property("redission_version")
|
||||||
|
implementation("org.redisson:redisson:${redissionVersion}") {
|
||||||
|
exclude(group = "io.netty")
|
||||||
|
exclude(group = "org.yaml")
|
||||||
|
exclude(group = "org.slf4j")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
processResources {
|
||||||
|
filesMatching("bungee.yml") {
|
||||||
|
expand(project.properties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
withType<Jar>() {
|
||||||
|
archiveBaseName = "HamsterBall-BungeeCord"
|
||||||
|
}
|
||||||
|
}
|
@@ -22,9 +22,9 @@ public class HamsterBallPlugin extends Plugin {
|
|||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
instance = this;
|
instance = this;
|
||||||
Logger logger = getLogger();
|
Logger logger = getLogger();
|
||||||
BallBungeeCordAPI.init();
|
|
||||||
logger.info("BallBungeeCordAPI 已初始化.");
|
|
||||||
try {
|
try {
|
||||||
|
BallBungeeCordAPI.init();
|
||||||
|
logger.info("BallBungeeCordAPI 已初始化.");
|
||||||
BallBungeeCordAPI.getInstance().enable();
|
BallBungeeCordAPI.getInstance().enable();
|
||||||
logger.info("BallBungeeCordAPI 已启动.");
|
logger.info("BallBungeeCordAPI 已启动.");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -36,7 +36,7 @@ public class HamsterBallPlugin extends Plugin {
|
|||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
Logger logger = getLogger();
|
Logger logger = getLogger();
|
||||||
if (!BallAPI.getInstance().isConnected()) {
|
if (!BallAPI.getInstance().isEnabled()) {
|
||||||
ProxyServer.getInstance().stop("由于 HamsterBall 未能成功连接, 服务器将立即关闭.");
|
ProxyServer.getInstance().stop("由于 HamsterBall 未能成功连接, 服务器将立即关闭.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -1,73 +1,83 @@
|
|||||||
package cn.hamster3.mc.plugin.core.bungee.api;
|
package cn.hamster3.mc.plugin.core.bungee.api;
|
||||||
|
|
||||||
import cn.hamster3.mc.plugin.ball.common.api.BallAPI;
|
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.codec.BallMessageInfoCodec;
|
||||||
|
import cn.hamster3.mc.plugin.ball.common.data.BallMessageInfo;
|
||||||
import cn.hamster3.mc.plugin.ball.common.entity.BallServerInfo;
|
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.entity.BallServerType;
|
||||||
import cn.hamster3.mc.plugin.ball.common.listener.BallDebugListener;
|
import cn.hamster3.mc.plugin.ball.common.listener.BallDebugListener;
|
||||||
import cn.hamster3.mc.plugin.core.bungee.HamsterBallPlugin;
|
import cn.hamster3.mc.plugin.core.bungee.HamsterBallPlugin;
|
||||||
import cn.hamster3.mc.plugin.core.bungee.listener.BallBungeeCordListener;
|
|
||||||
import cn.hamster3.mc.plugin.core.bungee.util.BallBungeeCordUtils;
|
import cn.hamster3.mc.plugin.core.bungee.util.BallBungeeCordUtils;
|
||||||
import cn.hamster3.mc.plugin.core.bungee.util.CoreBungeeCordUtils;
|
import cn.hamster3.mc.plugin.core.bungee.util.CoreBungeeCordUtils;
|
||||||
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
|
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
|
||||||
import net.md_5.bungee.config.Configuration;
|
import net.md_5.bungee.config.Configuration;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.redisson.Redisson;
|
||||||
|
import org.redisson.api.RTopic;
|
||||||
|
import org.redisson.api.RedissonClient;
|
||||||
|
import org.redisson.config.Config;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public class BallBungeeCordAPI extends BallAPI {
|
public class BallBungeeCordAPI extends BallAPI {
|
||||||
private DataSource datasource;
|
@Nullable
|
||||||
|
private final DataSource datasource;
|
||||||
|
@NotNull
|
||||||
|
private final RedissonClient redissonClient;
|
||||||
|
|
||||||
public BallBungeeCordAPI(@NotNull BallConfig config) {
|
public BallBungeeCordAPI(@NotNull BallServerInfo localServerInfo, @Nullable DataSource datasource, @NotNull RedissonClient redissonClient) {
|
||||||
super(config);
|
super(localServerInfo);
|
||||||
|
this.datasource = datasource;
|
||||||
|
this.redissonClient = redissonClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BallBungeeCordAPI getInstance() {
|
public static BallBungeeCordAPI getInstance() {
|
||||||
return (BallBungeeCordAPI) instance;
|
return (BallBungeeCordAPI) instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void init() {
|
public static void init() throws IOException {
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
HamsterBallPlugin plugin = HamsterBallPlugin.getInstance();
|
HamsterBallPlugin plugin = HamsterBallPlugin.getInstance();
|
||||||
Configuration pluginConfig = CoreBungeeCordUtils.getPluginConfig(plugin);
|
Configuration config = CoreBungeeCordUtils.getPluginConfig(plugin);
|
||||||
Map<String, String> env = System.getenv();
|
Map<String, String> env = System.getenv();
|
||||||
|
|
||||||
String serverInfoID = env.getOrDefault("BALL_LOCAL_SERVER_INFO_ID",
|
|
||||||
pluginConfig.getString("server-info.id"));
|
|
||||||
String serverInfoName = env.getOrDefault("BALL_LOCAL_SERVER_INFO_NAME",
|
|
||||||
pluginConfig.getString("server-info.name"));
|
|
||||||
String serverInfoHost = env.getOrDefault("BALL_LOCAL_SERVER_IP",
|
|
||||||
pluginConfig.getString("server-info.host", "0.0.0.0"));
|
|
||||||
int serverInfoPort = Integer.parseInt(env.getOrDefault("BALL_LOCAL_SERVER_PORT",
|
|
||||||
String.valueOf(pluginConfig.getInt("server-info.port", 25577))));
|
|
||||||
BallServerInfo serverInfo = new BallServerInfo(
|
BallServerInfo serverInfo = new BallServerInfo(
|
||||||
serverInfoID,
|
env.getOrDefault("BALL_LOCAL_SERVER_INFO_ID", config.getString("server-info.id")),
|
||||||
serverInfoName,
|
env.getOrDefault("BALL_LOCAL_SERVER_INFO_NAME", config.getString("server-info.name")),
|
||||||
BallServerType.PROXY,
|
BallServerType.PROXY,
|
||||||
serverInfoHost,
|
env.getOrDefault("BALL_LOCAL_SERVER_IP", config.getString("server-info.host", "0.0.0.0")),
|
||||||
serverInfoPort
|
Integer.parseInt(
|
||||||
|
env.getOrDefault("BALL_LOCAL_SERVER_PORT", config.getString("server-info.port", "25577"))
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
String serverHost = env.getOrDefault("BALL_SERVER_HOST",
|
DataSource datasource = BallBungeeCordUtils.getDataSource(config.getSection("datasource"));
|
||||||
pluginConfig.getString("ball-server.host", "ball.hamster3.cn"));
|
File redissionConfig = new File(plugin.getDataFolder(), "redission.yml");
|
||||||
int serverPort = Integer.parseInt(env.getOrDefault("BALL_SERVER_PORT",
|
if (!redissionConfig.exists()) {
|
||||||
String.valueOf(pluginConfig.getInt("ball-server.port", 58888))));
|
Files.copy(
|
||||||
int eventLoopThread = Integer.parseInt(env.getOrDefault("BALL_EVENT_LOOP_THREAD",
|
plugin.getResourceAsStream("redission.yml"),
|
||||||
String.valueOf(pluginConfig.getInt("ball-server.event-loop-thread", 2))));
|
redissionConfig.toPath(),
|
||||||
BallConfig config = new BallConfig(serverInfo, serverHost, serverPort, eventLoopThread);
|
StandardCopyOption.REPLACE_EXISTING
|
||||||
|
);
|
||||||
BallBungeeCordAPI apiInstance = new BallBungeeCordAPI(config);
|
|
||||||
apiInstance.datasource = BallBungeeCordUtils.getDataSource(pluginConfig.getSection("datasource"));
|
|
||||||
|
|
||||||
apiInstance.addListener(BallBungeeCordListener.INSTANCE);
|
|
||||||
if (pluginConfig.getBoolean("debug", false)) {
|
|
||||||
apiInstance.addListener(BallDebugListener.INSTANCE);
|
|
||||||
}
|
}
|
||||||
|
RedissonClient redissonClient = Redisson.create(Config.fromYAML(redissionConfig));
|
||||||
|
BallBungeeCordAPI apiInstance = new BallBungeeCordAPI(serverInfo, datasource, redissonClient);
|
||||||
|
|
||||||
|
if (config.getBoolean("debug", false)) {
|
||||||
|
RTopic topic = redissonClient.getTopic(BALL_CHANNEL, BallMessageInfoCodec.INSTANCE);
|
||||||
|
topic.addListener(BallMessageInfo.class, BallDebugListener.INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
instance = apiInstance;
|
instance = apiInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,4 +100,9 @@ public class BallBungeeCordAPI extends BallAPI {
|
|||||||
public @NotNull DataSource getDatasource() {
|
public @NotNull DataSource getDatasource() {
|
||||||
return datasource == null ? CoreAPI.getInstance().getDataSource() : datasource;
|
return datasource == null ? CoreAPI.getInstance().getDataSource() : datasource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull RedissonClient getRedissonClient() {
|
||||||
|
return redissonClient;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
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.BallLocation;
|
||||||
import cn.hamster3.mc.plugin.ball.common.data.BallMessageInfo;
|
import cn.hamster3.mc.plugin.ball.common.data.BallMessageInfo;
|
||||||
import cn.hamster3.mc.plugin.ball.common.entity.BallPlayerInfo;
|
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.event.server.ServerOnlineEvent;
|
||||||
import cn.hamster3.mc.plugin.ball.common.listener.BallListener;
|
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.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.data.DisplayMessage;
|
||||||
import cn.hamster3.mc.plugin.core.common.util.CoreUtils;
|
import cn.hamster3.mc.plugin.core.common.util.CoreUtils;
|
||||||
import cn.hamster3.mc.plugin.core.lib.net.kyori.adventure.text.Component;
|
import cn.hamster3.mc.plugin.core.lib.net.kyori.adventure.text.Component;
|
||||||
import com.google.gson.JsonElement;
|
import lombok.Getter;
|
||||||
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 org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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 javax.sql.DataSource;
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
@@ -48,44 +42,21 @@ public abstract class BallAPI {
|
|||||||
protected final Map<String, BallServerInfo> serverInfo;
|
protected final Map<String, BallServerInfo> serverInfo;
|
||||||
@NotNull
|
@NotNull
|
||||||
protected final Map<UUID, BallPlayerInfo> playerInfo;
|
protected final Map<UUID, BallPlayerInfo> playerInfo;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private final BallConfig config;
|
private final BallServerInfo localServerInfo;
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Bootstrap bootstrap;
|
|
||||||
@NotNull
|
|
||||||
private final EventLoopGroup executors;
|
|
||||||
protected Channel channel;
|
|
||||||
|
|
||||||
private boolean enabled;
|
|
||||||
private boolean connected;
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private List<BallListener> listeners;
|
private List<BallListener> listeners;
|
||||||
|
|
||||||
protected BallAPI(@NotNull BallConfig config) {
|
@Getter
|
||||||
this.config = config;
|
private boolean enabled;
|
||||||
this.enabled = false;
|
|
||||||
this.connected = false;
|
public BallAPI(@NotNull BallServerInfo localServerInfo) {
|
||||||
|
this.localServerInfo = localServerInfo;
|
||||||
|
|
||||||
serverInfo = new ConcurrentHashMap<>();
|
serverInfo = new ConcurrentHashMap<>();
|
||||||
playerInfo = new ConcurrentHashMap<>();
|
playerInfo = new ConcurrentHashMap<>();
|
||||||
listeners = new ArrayList<>();
|
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() {
|
addListener(new BallListener() {
|
||||||
@Override
|
@Override
|
||||||
public ListenerPriority getPriority() {
|
public ListenerPriority getPriority() {
|
||||||
@@ -158,11 +129,10 @@ public abstract class BallAPI {
|
|||||||
@Override
|
@Override
|
||||||
public void onConnectRefused() {
|
public void onConnectRefused() {
|
||||||
enabled = false;
|
enabled = false;
|
||||||
connected = false;
|
|
||||||
executors.shutdownGracefully();
|
|
||||||
getLogger().info("连接至服务中心的请求被拒绝,已关闭仓鼠球。");
|
getLogger().info("连接至服务中心的请求被拒绝,已关闭仓鼠球。");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -178,11 +148,8 @@ public abstract class BallAPI {
|
|||||||
if (enabled) {
|
if (enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
enabled = true;
|
|
||||||
BallServerInfo localInfo = getLocalServerInfo();
|
BallServerInfo localInfo = getLocalServerInfo();
|
||||||
|
|
||||||
connect();
|
|
||||||
|
|
||||||
try (Connection connection = BallAPI.getInstance().getDatasource().getConnection()) {
|
try (Connection connection = BallAPI.getInstance().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`(" +
|
||||||
@@ -246,68 +213,10 @@ public abstract class BallAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
enabled = true;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void disable() throws SQLException, InterruptedException {
|
protected void disable() throws SQLException, InterruptedException {
|
||||||
if (!enabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
enabled = false;
|
|
||||||
|
|
||||||
sendBallMessage(new BallMessageInfo(BALL_CHANNEL, ServerOfflineEvent.ACTION, new ServerOfflineEvent(getLocalServerId())), true);
|
sendBallMessage(new BallMessageInfo(BALL_CHANNEL, ServerOfflineEvent.ACTION, new ServerOfflineEvent(getLocalServerId())), true);
|
||||||
|
|
||||||
try (Connection connection = BallAPI.getInstance().getDatasource().getConnection()) {
|
try (Connection connection = BallAPI.getInstance().getDatasource().getConnection()) {
|
||||||
@@ -325,10 +234,7 @@ public abstract class BallAPI {
|
|||||||
statement.executeUpdate();
|
statement.executeUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
getRedissonClient().shutdown();
|
||||||
channel = null;
|
|
||||||
connected = false;
|
|
||||||
executors.shutdownGracefully().await();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -608,28 +514,6 @@ public abstract class BallAPI {
|
|||||||
sendBallMessage(new BallMessageInfo(channel, action));
|
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 则必须等待消息写入网络的操作完成后,该方法才会退出)
|
* @param block 是否阻塞(设置为 true 则必须等待消息写入网络的操作完成后,该方法才会退出)
|
||||||
*/
|
*/
|
||||||
public void sendBallMessage(@NotNull BallMessageInfo messageInfo, boolean block) {
|
public void sendBallMessage(@NotNull BallMessageInfo messageInfo, boolean block) {
|
||||||
if (channel == null || !channel.isWritable()) {
|
String string = CoreUtils.GSON.toJson(messageInfo);
|
||||||
getLogger().warning("由于服务不可用,有一条消息发送失败了: " + messageInfo);
|
RTopic topic = getRedissonClient().getTopic(BallAPI.BALL_CHANNEL, StringCodec.INSTANCE);
|
||||||
return;
|
|
||||||
}
|
|
||||||
ChannelFuture future = channel.writeAndFlush(CoreUtils.GSON.toJson(messageInfo));
|
|
||||||
if (block) {
|
if (block) {
|
||||||
try {
|
topic.publish(string);
|
||||||
future.await();
|
} else {
|
||||||
} catch (InterruptedException e) {
|
topic.publishAsync(string);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
for (BallListener listener : BallAPI.getInstance().getListeners()) {
|
||||||
try {
|
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) {
|
public void addListener(@NotNull BallListener listener) {
|
||||||
ArrayList<BallListener> newListeners = new ArrayList<>(listeners);
|
ArrayList<BallListener> newListeners = new ArrayList<>(listeners);
|
||||||
newListeners.add(listener);
|
newListeners.add(listener);
|
||||||
@@ -728,12 +577,12 @@ public abstract class BallAPI {
|
|||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public BallServerInfo getLocalServerInfo() {
|
public BallServerInfo getLocalServerInfo() {
|
||||||
return config.getLocalInfo();
|
return localServerInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public String getLocalServerId() {
|
public String getLocalServerId() {
|
||||||
return config.getLocalInfo().getId();
|
return localServerInfo.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -848,11 +697,6 @@ public abstract class BallAPI {
|
|||||||
return info.getName();
|
return info.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
|
||||||
public boolean isConnected() {
|
|
||||||
return connected;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public Map<String, BallServerInfo> getAllServerInfo() {
|
public Map<String, BallServerInfo> getAllServerInfo() {
|
||||||
return serverInfo;
|
return serverInfo;
|
||||||
@@ -873,4 +717,7 @@ public abstract class BallAPI {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public abstract DataSource getDatasource();
|
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.JsonArray;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonPrimitive;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -69,21 +68,7 @@ public class BallMessageInfo {
|
|||||||
this.action = action;
|
this.action = action;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BallMessageInfo(@NotNull String channel, @NotNull String action, String content) {
|
public BallMessageInfo(@NotNull String channel, @NotNull String action, @NotNull Object 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) {
|
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
senderID = BallAPI.getInstance().getLocalServerId();
|
senderID = BallAPI.getInstance().getLocalServerId();
|
||||||
this.action = action;
|
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.api.BallAPI;
|
||||||
import cn.hamster3.mc.plugin.ball.common.data.BallMessageInfo;
|
import cn.hamster3.mc.plugin.ball.common.data.BallMessageInfo;
|
||||||
import cn.hamster3.mc.plugin.ball.common.event.player.*;
|
import org.redisson.api.listener.MessageListener;
|
||||||
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 java.util.logging.Level;
|
public class BallDebugListener implements MessageListener<BallMessageInfo> {
|
||||||
|
|
||||||
public final class BallDebugListener implements BallListener {
|
|
||||||
public static final BallDebugListener INSTANCE = new BallDebugListener();
|
public static final BallDebugListener INSTANCE = new BallDebugListener();
|
||||||
|
|
||||||
private BallDebugListener() {
|
private BallDebugListener() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListenerPriority getPriority() {
|
public void onMessage(CharSequence channel, BallMessageInfo event) {
|
||||||
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) {
|
|
||||||
BallAPI.getInstance().getLogger().info("收到了一条消息: " + 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
|
@@ -1,61 +0,0 @@
|
|||||||
setArchivesBaseName("HamsterBall-Server")
|
|
||||||
|
|
||||||
evaluationDependsOn(':ball-common')
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
apiShade project(":ball-common") transitive false
|
|
||||||
shade "cn.hamster3.mc.plugin:core-common:${hamster_core_version}"
|
|
||||||
|
|
||||||
// // https://mvnrepository.com/artifact/org.slf4j/slf4j-api
|
|
||||||
// implementation 'org.slf4j:slf4j-api:2.0.3'
|
|
||||||
|
|
||||||
// https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core
|
|
||||||
implementationShade 'org.apache.logging.log4j:log4j-core:2.19.0'
|
|
||||||
// https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl
|
|
||||||
implementationShade 'org.apache.logging.log4j:log4j-slf4j-impl:2.19.0'
|
|
||||||
|
|
||||||
// https://mvnrepository.com/artifact/io.netty/netty-all
|
|
||||||
implementationShade 'io.netty:netty-all:4.1.86.Final'
|
|
||||||
// https://mvnrepository.com/artifact/org.yaml/snakeyaml
|
|
||||||
implementationShade 'org.yaml:snakeyaml:2.0'
|
|
||||||
// https://mvnrepository.com/artifact/com.google.code.gson/gson
|
|
||||||
//noinspection GradlePackageUpdate
|
|
||||||
implementationShade 'com.google.code.gson:gson:2.8.9'
|
|
||||||
|
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
|
|
||||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
|
|
||||||
}
|
|
||||||
|
|
||||||
test {
|
|
||||||
useJUnitPlatform()
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register("shadowJar", Jar) {
|
|
||||||
dependsOn("jar")
|
|
||||||
manifest.attributes('Main-Class': 'cn.hamster3.mc.plugin.ball.server.Bootstrap')
|
|
||||||
manifest.attributes('ball-version': project.version)
|
|
||||||
from([
|
|
||||||
tasks.jar.outputs.files.collect {
|
|
||||||
it.isDirectory() ? it : zipTree(it)
|
|
||||||
},
|
|
||||||
configurations.shade.collect {
|
|
||||||
it.isDirectory() ? it : zipTree(it)
|
|
||||||
},
|
|
||||||
configurations.apiShade.collect {
|
|
||||||
it.isDirectory() ? it : zipTree(it)
|
|
||||||
},
|
|
||||||
configurations.implementationShade.collect {
|
|
||||||
it.isDirectory() ? it : zipTree(it)
|
|
||||||
}
|
|
||||||
])
|
|
||||||
destinationDirectory = rootProject.buildDir
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks {
|
|
||||||
compileJava {
|
|
||||||
dependsOn(":ball-common:build")
|
|
||||||
}
|
|
||||||
build {
|
|
||||||
dependsOn(shadowJar)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,77 +0,0 @@
|
|||||||
package cn.hamster3.mc.plugin.ball.server;
|
|
||||||
|
|
||||||
import cn.hamster3.mc.plugin.ball.common.utils.OS;
|
|
||||||
import cn.hamster3.mc.plugin.ball.server.command.CommandHandler;
|
|
||||||
import cn.hamster3.mc.plugin.ball.server.config.ServerConfig;
|
|
||||||
import cn.hamster3.mc.plugin.ball.server.connector.BallServerChannelInitializer;
|
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelOption;
|
|
||||||
import io.netty.channel.EventLoopGroup;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.lang.management.ManagementFactory;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.StandardCopyOption;
|
|
||||||
|
|
||||||
public class Bootstrap {
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger("Bootstrap");
|
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException, InterruptedException {
|
|
||||||
if (initDefaultFile()) {
|
|
||||||
LOGGER.info("请重新启动该程序.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ServerConfig.init();
|
|
||||||
LOGGER.info("配置文件加载完成.");
|
|
||||||
OS currentOS = OS.getCurrentOS();
|
|
||||||
LOGGER.info("当前操作系统为: {}. 选择 IO 模式为: {}", currentOS.name(), currentOS.getIOModeName());
|
|
||||||
EventLoopGroup loopGroup = currentOS.getEventLoopGroup(ServerConfig.getEventLoopThread());
|
|
||||||
ServerBootstrap bootstrap = new ServerBootstrap()
|
|
||||||
.group(loopGroup)
|
|
||||||
.channel(currentOS.getServerSocketChannel())
|
|
||||||
.childOption(ChannelOption.TCP_NODELAY, true)
|
|
||||||
.childHandler(new BallServerChannelInitializer());
|
|
||||||
ChannelFuture future = bootstrap.bind(ServerConfig.getHost(), ServerConfig.getPort());
|
|
||||||
future.await();
|
|
||||||
if (future.isSuccess()) {
|
|
||||||
LOGGER.info("进程信息: {}", ManagementFactory.getRuntimeMXBean().getName());
|
|
||||||
LOGGER.info("服务器已启动. 输入 stop 来关闭该程序.");
|
|
||||||
} else {
|
|
||||||
LOGGER.error("仓鼠球服务器启动失败!", future.cause());
|
|
||||||
loopGroup.shutdownGracefully();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CommandHandler.INSTANCE.start(loopGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean initDefaultFile() throws IOException {
|
|
||||||
boolean saved = false;
|
|
||||||
File log4jFile = new File("log4j2.xml");
|
|
||||||
if (!log4jFile.exists()) {
|
|
||||||
InputStream stream = ServerConfig.class.getResourceAsStream("/log4j2.xml");
|
|
||||||
if (stream == null) {
|
|
||||||
throw new IOException("log4j2.xml 文件损坏!");
|
|
||||||
}
|
|
||||||
Files.copy(stream, log4jFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
LOGGER.info("已生成默认 log4j2.xml 文件!");
|
|
||||||
saved = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
File configFile = new File("config.yml");
|
|
||||||
if (!configFile.exists()) {
|
|
||||||
InputStream stream = ServerConfig.class.getResourceAsStream("/config.yml");
|
|
||||||
if (stream == null) {
|
|
||||||
throw new IOException("config.yml 文件损坏!");
|
|
||||||
}
|
|
||||||
Files.copy(stream, configFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
LOGGER.info("已生成默认 config.yml 文件!");
|
|
||||||
saved = true;
|
|
||||||
}
|
|
||||||
return saved;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,79 +0,0 @@
|
|||||||
package cn.hamster3.mc.plugin.ball.server.command;
|
|
||||||
|
|
||||||
import cn.hamster3.mc.plugin.ball.server.config.ServerConfig;
|
|
||||||
import io.netty.channel.EventLoopGroup;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Scanner;
|
|
||||||
|
|
||||||
public class CommandHandler {
|
|
||||||
public static final CommandHandler INSTANCE = new CommandHandler();
|
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger("CommandHandler");
|
|
||||||
private EventLoopGroup loopGroup;
|
|
||||||
|
|
||||||
private boolean started;
|
|
||||||
|
|
||||||
public void start(EventLoopGroup loopGroup) {
|
|
||||||
this.loopGroup = loopGroup;
|
|
||||||
|
|
||||||
started = true;
|
|
||||||
Scanner scanner = new Scanner(System.in);
|
|
||||||
LOGGER.info("命令执行器准备就绪. 输入 help 查看命令帮助.");
|
|
||||||
while (started) {
|
|
||||||
String command = scanner.nextLine();
|
|
||||||
try {
|
|
||||||
executeCommand(command);
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.error("执行命令 " + command + " 时遇到了一个异常: ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void executeCommand(String command) throws Exception {
|
|
||||||
String[] args = command.split(" ");
|
|
||||||
switch (args[0].toLowerCase()) {
|
|
||||||
case "?":
|
|
||||||
case "help": {
|
|
||||||
help();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "reload": {
|
|
||||||
reload();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "end":
|
|
||||||
case "stop": {
|
|
||||||
stop();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
LOGGER.info("未知指令. 请输入 help 查看帮助.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void help() {
|
|
||||||
LOGGER.info("===============================================================");
|
|
||||||
LOGGER.info("help - 查看帮助.");
|
|
||||||
LOGGER.info("reload - 重载配置文件.");
|
|
||||||
LOGGER.info("stop - 关闭该程序.");
|
|
||||||
LOGGER.info("===============================================================");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reload() throws IOException {
|
|
||||||
ServerConfig.init();
|
|
||||||
LOGGER.info("配置文件加载完成.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stop() throws Exception {
|
|
||||||
started = false;
|
|
||||||
LOGGER.info("准备关闭服务器...");
|
|
||||||
loopGroup.shutdownGracefully().await();
|
|
||||||
LOGGER.info("服务器已关闭!");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,66 +0,0 @@
|
|||||||
package cn.hamster3.mc.plugin.ball.server.config;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.yaml.snakeyaml.Yaml;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public final class ServerConfig {
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger("ServerConfig");
|
|
||||||
|
|
||||||
private static String host;
|
|
||||||
private static int port;
|
|
||||||
private static int eventLoopThread;
|
|
||||||
private static boolean enableAcceptList;
|
|
||||||
private static List<String> acceptList;
|
|
||||||
|
|
||||||
private ServerConfig() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static void init() throws IOException {
|
|
||||||
File configFile = new File("config.yml");
|
|
||||||
Map<String, Object> map;
|
|
||||||
try (InputStream stream = Files.newInputStream(configFile.toPath())) {
|
|
||||||
map = new Yaml().load(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
host = map.getOrDefault("host", "localhost").toString();
|
|
||||||
port = (int) map.getOrDefault("port", 58888);
|
|
||||||
eventLoopThread = (int) map.getOrDefault("event-loop-thread", 5);
|
|
||||||
enableAcceptList = (boolean) map.get("enable-accept-list");
|
|
||||||
acceptList = (List<String>) map.get("accept-list");
|
|
||||||
|
|
||||||
LOGGER.info("host: {}", host);
|
|
||||||
LOGGER.info("port: {}", port);
|
|
||||||
LOGGER.info("eventLoopThread: {}", eventLoopThread);
|
|
||||||
LOGGER.info("enableAcceptList: {}", enableAcceptList);
|
|
||||||
LOGGER.info("acceptList: {}", acceptList);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getHost() {
|
|
||||||
return host;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getPort() {
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getEventLoopThread() {
|
|
||||||
return eventLoopThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isEnableAcceptList() {
|
|
||||||
return enableAcceptList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<String> getAcceptList() {
|
|
||||||
return acceptList;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,49 +0,0 @@
|
|||||||
package cn.hamster3.mc.plugin.ball.server.connector;
|
|
||||||
|
|
||||||
import cn.hamster3.mc.plugin.ball.common.data.BallMessageInfo;
|
|
||||||
import cn.hamster3.mc.plugin.ball.server.constant.ConstantObjects;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.SimpleChannelInboundHandler;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
@ChannelHandler.Sharable
|
|
||||||
public class BallServerChannelHandler extends SimpleChannelInboundHandler<String> {
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger("BallServerChannelHandler");
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void channelRead0(ChannelHandlerContext context, String message) {
|
|
||||||
if ("ping".equals(message)) {
|
|
||||||
context.channel().writeAndFlush("pong");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
BallMessageInfo messageInfo = ConstantObjects.GSON.fromJson(message, BallMessageInfo.class);
|
|
||||||
LOGGER.info("从服务器 {} 上收到一条消息: {}", context.channel().remoteAddress(), messageInfo);
|
|
||||||
BallServerChannelInitializer.broadcastMessage(messageInfo);
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.error(String.format("处理消息 %s 时出现错误: ", message), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelActive(@NotNull ChannelHandlerContext context) {
|
|
||||||
LOGGER.info("与服务器 {} 建立了连接.", context.channel().remoteAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void channelInactive(ChannelHandlerContext context) {
|
|
||||||
context.close();
|
|
||||||
synchronized (BallServerChannelInitializer.CHANNELS) {
|
|
||||||
BallServerChannelInitializer.CHANNELS.remove(context.channel());
|
|
||||||
}
|
|
||||||
LOGGER.warn("与服务器 {} 的连接已断开.", context.channel().remoteAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(ChannelHandlerContext context, Throwable cause) {
|
|
||||||
LOGGER.warn("与服务器 {} 通信时出现了一个错误: ", context.channel().remoteAddress(), cause);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,73 +0,0 @@
|
|||||||
package cn.hamster3.mc.plugin.ball.server.connector;
|
|
||||||
|
|
||||||
import cn.hamster3.mc.plugin.ball.common.data.BallMessageInfo;
|
|
||||||
import cn.hamster3.mc.plugin.ball.server.config.ServerConfig;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
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 org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class BallServerChannelInitializer extends ChannelInitializer<SocketChannel> {
|
|
||||||
public static final List<Channel> CHANNELS = new ArrayList<>();
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger("BallServerChannelInitializer");
|
|
||||||
|
|
||||||
public static void broadcastMessage(BallMessageInfo messageInfo) {
|
|
||||||
String string = messageInfo.toString();
|
|
||||||
synchronized (CHANNELS) {
|
|
||||||
for (Channel channel : CHANNELS) {
|
|
||||||
channel.writeAndFlush(string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initChannel(@NotNull SocketChannel channel) {
|
|
||||||
InetSocketAddress remoteAddress = channel.remoteAddress();
|
|
||||||
LOGGER.info("远程地址 {} 请求建立连接.", remoteAddress.toString());
|
|
||||||
|
|
||||||
String hostAddress = remoteAddress.getAddress().getHostAddress();
|
|
||||||
if (ServerConfig.isEnableAcceptList() && !ServerConfig.getAcceptList().contains(hostAddress)) {
|
|
||||||
LOGGER.warn("{} 不在白名单列表中, 断开连接!", hostAddress);
|
|
||||||
|
|
||||||
channel.pipeline()
|
|
||||||
.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));
|
|
||||||
|
|
||||||
try {
|
|
||||||
channel.writeAndFlush("connection refused").await();
|
|
||||||
channel.disconnect().await();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.pipeline()
|
|
||||||
.addLast(new IdleStateHandler(60, 0, 0, TimeUnit.SECONDS))
|
|
||||||
.addLast(new BallServerKeepAliveHandler())
|
|
||||||
.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 BallServerChannelHandler());
|
|
||||||
|
|
||||||
synchronized (CHANNELS) {
|
|
||||||
CHANNELS.add(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,22 +0,0 @@
|
|||||||
package cn.hamster3.mc.plugin.ball.server.connector;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandler;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.SimpleUserEventChannelHandler;
|
|
||||||
import io.netty.handler.timeout.IdleStateEvent;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
@ChannelHandler.Sharable
|
|
||||||
public class BallServerKeepAliveHandler extends SimpleUserEventChannelHandler<IdleStateEvent> {
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger("BallServerKeepAliveHandler");
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void eventReceived(ChannelHandlerContext context, IdleStateEvent event) {
|
|
||||||
synchronized (BallServerChannelInitializer.CHANNELS) {
|
|
||||||
BallServerChannelInitializer.CHANNELS.remove(context.channel());
|
|
||||||
}
|
|
||||||
context.close();
|
|
||||||
LOGGER.warn("由于无法验证连接存活,与服务器 {} 的连接已断开.", context.channel().remoteAddress());
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,12 +0,0 @@
|
|||||||
package cn.hamster3.mc.plugin.ball.server.constant;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
|
|
||||||
public interface ConstantObjects {
|
|
||||||
/**
|
|
||||||
* GSON 工具
|
|
||||||
*/
|
|
||||||
Gson GSON = new GsonBuilder().create();
|
|
||||||
|
|
||||||
}
|
|
@@ -1,17 +0,0 @@
|
|||||||
# 绑定网卡地址
|
|
||||||
host: "0.0.0.0"
|
|
||||||
# 绑定端口
|
|
||||||
port: 58888
|
|
||||||
|
|
||||||
# 线程池数量
|
|
||||||
# 建议设置为全服最大玩家数 / 20
|
|
||||||
# 不建议低于 4
|
|
||||||
event-loop-thread: 5
|
|
||||||
|
|
||||||
# 是否启用 IP 白名单
|
|
||||||
enable-accept-list: true
|
|
||||||
|
|
||||||
# 允许连接至服务的 ip 名单
|
|
||||||
accept-list:
|
|
||||||
- "127.0.0.1"
|
|
||||||
- "0:0:0:0:0:0:0:1"
|
|
@@ -1,2 +0,0 @@
|
|||||||
log4j2.loggerContextFactory=org.apache.logging.log4j.core.impl.Log4jContextFactory
|
|
||||||
log4j.configurationFile=log4j2.xml
|
|
@@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Configuration status="WARN" packages="com.mojang.util">
|
|
||||||
<Appenders>
|
|
||||||
<Console name="SysOut" target="SYSTEM_OUT">
|
|
||||||
<PatternLayout pattern="[%d{HH:mm:ss}] [%logger{1}/%level]: %msg%n"/>
|
|
||||||
</Console>
|
|
||||||
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
|
||||||
<PatternLayout charset="UTF-8" pattern="[%d{HH:mm:ss}] [%logger/%level]: %msg%n"/>
|
|
||||||
<Policies>
|
|
||||||
<TimeBasedTriggeringPolicy/>
|
|
||||||
<OnStartupTriggeringPolicy/>
|
|
||||||
</Policies>
|
|
||||||
<DefaultRolloverStrategy max="1000"/>
|
|
||||||
</RollingRandomAccessFile>
|
|
||||||
</Appenders>
|
|
||||||
<Loggers>
|
|
||||||
<Root level="info">
|
|
||||||
<AppenderRef ref="SysOut" level="info"/>
|
|
||||||
<AppenderRef ref="File"/>
|
|
||||||
</Root>
|
|
||||||
</Loggers>
|
|
||||||
</Configuration>
|
|
85
build.gradle
85
build.gradle
@@ -1,85 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'java'
|
|
||||||
}
|
|
||||||
|
|
||||||
group 'cn.hamster3.mc.plugin'
|
|
||||||
version '1.0.0-SNAPSHOT'
|
|
||||||
|
|
||||||
subprojects {
|
|
||||||
apply plugin: 'java-library'
|
|
||||||
apply plugin: 'maven-publish'
|
|
||||||
|
|
||||||
group = rootProject.group
|
|
||||||
version = rootProject.version
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
maven {
|
|
||||||
url = "https://maven.airgame.net/maven-public/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations {
|
|
||||||
shade
|
|
||||||
api.extendsFrom apiShade
|
|
||||||
implementation.extendsFrom implementationShade
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// https://mvnrepository.com/artifact/org.jetbrains/annotations
|
|
||||||
compileOnly 'org.jetbrains:annotations:23.0.0'
|
|
||||||
// https://mvnrepository.com/artifact/org.projectlombok/lombok
|
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.24'
|
|
||||||
annotationProcessor 'org.projectlombok:lombok:1.18.24'
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
// withJavadocJar()
|
|
||||||
withSourcesJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks {
|
|
||||||
withType(JavaCompile).configureEach {
|
|
||||||
options.setEncoding("UTF-8")
|
|
||||||
}
|
|
||||||
withType(Jar).configureEach {
|
|
||||||
from([rootProject.file("LICENSE")])
|
|
||||||
duplicatesStrategy(DuplicatesStrategy.EXCLUDE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
javadoc {
|
|
||||||
options.quiet()
|
|
||||||
options.setEncoding("UTF-8")
|
|
||||||
options.jFlags("-Dfile.encoding=utf8")
|
|
||||||
options.addStringOption('Xdoclint:none', '-quiet')
|
|
||||||
options.links = [
|
|
||||||
"https://javadoc.io/doc/org.jetbrains/annotations/23.0.0",
|
|
||||||
'https://javadoc.io/doc/com.google.code.gson/gson/2.8.0',
|
|
||||||
'http://milkbowl.github.io/VaultAPI',
|
|
||||||
'https://bukkit.windit.net/javadoc'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
mavenJava(MavenPublication) {
|
|
||||||
from project.components.java
|
|
||||||
}
|
|
||||||
}
|
|
||||||
repositories {
|
|
||||||
maven {
|
|
||||||
def releasesRepoUrl = 'https://maven.airgame.net/maven-releases/'
|
|
||||||
def snapshotsRepoUrl = 'https://maven.airgame.net/maven-snapshots/'
|
|
||||||
|
|
||||||
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
|
|
||||||
|
|
||||||
credentials {
|
|
||||||
username = rootProject.properties.getOrDefault("maven_username", "")
|
|
||||||
password = rootProject.properties.getOrDefault("maven_password", "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
83
build.gradle.kts
Normal file
83
build.gradle.kts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
plugins {
|
||||||
|
id("java")
|
||||||
|
id("maven-publish")
|
||||||
|
id("com.github.johnrengelman.shadow") version "8+"
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "cn.hamster3.mc.plugin"
|
||||||
|
version = "1.1.0-SNAPSHOT"
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
apply {
|
||||||
|
plugin("java")
|
||||||
|
plugin("maven-publish")
|
||||||
|
plugin("com.github.johnrengelman.shadow")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = rootProject.group
|
||||||
|
version = rootProject.version
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
url = uri("https://maven.airgame.net/maven-public/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// https://mvnrepository.com/artifact/org.jetbrains/annotations
|
||||||
|
implementation("org.jetbrains:annotations:24.0.1")
|
||||||
|
// https://mvnrepository.com/artifact/org.projectlombok/lombok
|
||||||
|
compileOnly("org.projectlombok:lombok:1.18.28")
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.28")
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
// withJavadocJar()
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks {
|
||||||
|
withType<JavaCompile>().configureEach {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
withType<Jar>().configureEach {
|
||||||
|
from(rootProject.file("LICENSE"))
|
||||||
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
|
}
|
||||||
|
javadoc {
|
||||||
|
options.jFlags("-Dfile.encoding=utf8")
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
options.quiet()
|
||||||
|
val o: StandardJavadocDocletOptions = options as StandardJavadocDocletOptions
|
||||||
|
o.addStringOption("Xdoclint:none", "-quiet")
|
||||||
|
o.links(
|
||||||
|
"https://javadoc.io/doc/org.jetbrains/annotations/23.0.0",
|
||||||
|
"https://javadoc.io/doc/com.google.code.gson/gson/2.8.0",
|
||||||
|
"https://bukkit.windit.net/javadoc"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
create<MavenPublication>("mavenJava") {
|
||||||
|
from(components["java"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
maven {
|
||||||
|
val releasesRepoUrl = uri("https://maven.airgame.net/maven-releases/")
|
||||||
|
val snapshotsRepoUrl = uri("https://maven.airgame.net/maven-snapshots/")
|
||||||
|
|
||||||
|
url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
|
||||||
|
|
||||||
|
credentials {
|
||||||
|
username = rootProject.properties.getOrDefault("maven_username", "").toString()
|
||||||
|
password = rootProject.properties.getOrDefault("maven_password", "").toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,2 +1,3 @@
|
|||||||
org.gradle.jvmargs=-Xmx2G
|
org.gradle.jvmargs=-Xmx2G
|
||||||
hamster_core_version=1.0.0-SNAPSHOT
|
hamster_core_version=1.0.0-SNAPSHOT
|
||||||
|
redission_version=3.23.2
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
1
gradle/wrapper/gradle-wrapper.properties
vendored
1
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
#Sun Aug 20 16:53:32 CST 2023
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||||
|
@@ -1,6 +0,0 @@
|
|||||||
rootProject.name = 'hamster-ball'
|
|
||||||
include 'ball-common'
|
|
||||||
include 'ball-bukkit'
|
|
||||||
include 'ball-bungeecord'
|
|
||||||
include 'ball-server'
|
|
||||||
|
|
4
settings.gradle.kts
Normal file
4
settings.gradle.kts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
rootProject.name = "hamster-ball"
|
||||||
|
include("ball-bukkit")
|
||||||
|
include("ball-common")
|
||||||
|
include("ball-bungeecord")
|
Reference in New Issue
Block a user