3 Commits

Author SHA1 Message Date
68747f76bb feat: 为 BungeeCord 添加完整的 netty 并重定向
All checks were successful
Publish Project / build (push) Successful in 1m26s
2025-07-03 03:52:50 +08:00
1c83615b7e docs: 修正文档
[skip ci]
2025-07-03 02:33:16 +08:00
1648a56453 feat: 添加 lettuce-core 用于连接 redis
All checks were successful
Publish Project / build (push) Successful in 2m43s
2025-07-03 02:29:02 +08:00
12 changed files with 111 additions and 35 deletions

View File

@@ -9,8 +9,6 @@
3. 命令行窗口中执行`./gradlew clean build` 3. 命令行窗口中执行`./gradlew clean build`
4. 构建成品在 `build` 文件夹 4. 构建成品在 `build` 文件夹
也可访问我的[Jenkins网站](https://jenkins.airgame.net/job/opensource/job/hamster-core/)获取最新版
# 开发 # 开发
## 添加依赖 ## 添加依赖
@@ -26,9 +24,9 @@ repositories {
dependencies { dependencies {
// 对于 Bukkit 插件 // 对于 Bukkit 插件
compileOnly("cn.hamster3.mc.plugin:core-bukkit:1.3.4-SNAPSHOT") compileOnly("cn.hamster3.mc.plugin:core-bukkit:1.4.0")
// 对于 BungeeCord 插件 // 对于 BungeeCord 插件
compileOnly("cn.hamster3.mc.plugin:core-bungee:1.3.4-SNAPSHOT") compileOnly("cn.hamster3.mc.plugin:core-bungee:1.4.0")
} }
``` ```
@@ -54,13 +52,13 @@ dependencies {
<dependency> <dependency>
<groupId>cn.hamster3.mc.plugin</groupId> <groupId>cn.hamster3.mc.plugin</groupId>
<artifactId>core-bukkit</artifactId> <artifactId>core-bukkit</artifactId>
<version>1.3.4-SNAPSHOT</version> <version>1.4.0</version>
</dependency> </dependency>
<!--对于 BungeeCord 插件--> <!--对于 BungeeCord 插件-->
<dependency> <dependency>
<groupId>cn.hamster3.mc.plugin</groupId> <groupId>cn.hamster3.mc.plugin</groupId>
<artifactId>core-bungee</artifactId> <artifactId>core-bungee</artifactId>
<version>1.3.4-SNAPSHOT</version> <version>1.4.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
@@ -74,10 +72,4 @@ dependencies {
- 使用方法为:`CoreAPI.getInstance().xxx()` - 使用方法为:`CoreAPI.getInstance().xxx()`
- 部分通用的工具代码在 `cn.hamster3.mc.plugin.core.common.util` 包中 - 部分通用的工具代码在 `cn.hamster3.mc.plugin.core.common.util` 包中
- 部分Bukkit的工具代码在 `cn.hamster3.mc.plugin.core.bukkit.util` 包中 - 部分Bukkit的工具代码在 `cn.hamster3.mc.plugin.core.bukkit.util` 包中
- 部分BungeeCord的工具代码在 `cn.hamster3.mc.plugin.core.bungee.util` 包中 - 部分BungeeCord的工具代码在 `cn.hamster3.mc.plugin.core.bungee.util` 包中
# 已知问题
部分 Windows 服务器在启动时偶尔会遇到 Redis 链接失败的问题
如果服务器启动时遇到报错 `Caused by: java.io.IOException: Unable to establish loopback connection`
可以通过在启动脚本中添加 `-Djava.net.preferIPv4Stack=true` 参数解决

View File

@@ -5,7 +5,7 @@ plugins {
} }
group = "cn.hamster3.mc.plugin" group = "cn.hamster3.mc.plugin"
version = "1.3.5" version = "1.4.0"
description = "叁只仓鼠的 Minecraft 插件开发通用工具包" description = "叁只仓鼠的 Minecraft 插件开发通用工具包"
subprojects { subprojects {

View File

@@ -1,6 +1,9 @@
@file:Suppress("VulnerableLibrariesLocal") @file:Suppress("VulnerableLibrariesLocal")
evaluationDependsOn(":core-common") evaluationDependsOn(":core-common")
evaluationDependsOn(":core-relocate-lettuce")
val shade = configurations.create("shade")
dependencies { dependencies {
api(project(":core-common")) { isTransitive = false } api(project(":core-common")) { isTransitive = false }
@@ -10,26 +13,31 @@ dependencies {
compileOnly("org.black_ixx:playerpoints:3.2.6") { isTransitive = false } compileOnly("org.black_ixx:playerpoints:3.2.6") { isTransitive = false }
compileOnly("me.clip:placeholderapi:2.11.5") { isTransitive = false } compileOnly("me.clip:placeholderapi:2.11.5") { isTransitive = false }
// https://www.spigotmc.org/resources/nbt-api.7939/
implementation("de.tr7zw:item-nbt-api:2.15.1")
api("net.kyori:adventure-platform-bukkit:4.3.2") { api("net.kyori:adventure-platform-bukkit:4.3.2") {
exclude(group = "org.jetbrains") exclude(group = "org.jetbrains")
exclude(group = "com.google.code.gson") exclude(group = "com.google.code.gson")
exclude(group = "io.netty")
} }
api("net.kyori:adventure-text-minimessage:4.15.0") { api("net.kyori:adventure-text-minimessage:4.15.0") {
exclude(module = "adventure-api") exclude(module = "adventure-api")
exclude(group = "org.jetbrains") exclude(group = "org.jetbrains")
exclude(group = "io.netty")
} }
// https://mvnrepository.com/artifact/redis.clients/jedis
implementation("com.zaxxer:HikariCP:4.0.3") { exclude(group = "org.slf4j") }
api("redis.clients:jedis:5.1.4") { api("redis.clients:jedis:5.1.4") {
exclude(group = "com.google.code.gson") exclude(group = "com.google.code.gson")
exclude(group = "org.slf4j") exclude(group = "org.slf4j")
} }
compileOnlyApi("io.lettuce:lettuce-core:6.7.1.RELEASE") {
// https://www.spigotmc.org/resources/nbt-api.7939/ exclude(group = "org.slf4j")
implementation("de.tr7zw:item-nbt-api:2.13.2") exclude(group = "io.netty")
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP }
implementation("com.zaxxer:HikariCP:4.0.3") { exclude(group = "org.slf4j") } shade(project(":core-relocate-lettuce"))
} }
tasks { tasks {
processResources { processResources {
filesMatching("plugin.yml") { filesMatching("plugin.yml") {
@@ -40,6 +48,13 @@ tasks {
archiveBaseName = "HamsterCore-Bukkit" archiveBaseName = "HamsterCore-Bukkit"
} }
shadowJar { shadowJar {
dependsOn(":core-relocate-lettuce:shadowJar")
val task = project(":core-relocate-lettuce").tasks.shadowJar.get()
from(task.outputs.files.map {
if (it.isDirectory) it else zipTree(
it
)
})
destinationDirectory = rootProject.layout.buildDirectory destinationDirectory = rootProject.layout.buildDirectory
relocate("de.tr7zw.changeme.nbtapi", "cn.hamster3.mc.plugin.core.lib.de.tr7zw.nbtapi") relocate("de.tr7zw.changeme.nbtapi", "cn.hamster3.mc.plugin.core.lib.de.tr7zw.nbtapi")
relocate("de.tr7zw.annotations", "cn.hamster3.mc.plugin.core.lib.de.tr7zw.nbtapi.annotations") relocate("de.tr7zw.annotations", "cn.hamster3.mc.plugin.core.lib.de.tr7zw.nbtapi.annotations")

View File

@@ -132,6 +132,7 @@ public class HamsterCorePlugin extends JavaPlugin {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
if (CoreAPI.getInstance().isEnableRedis()) { if (CoreAPI.getInstance().isEnableRedis()) {
CoreAPI.getInstance().getJedisPool().close(); CoreAPI.getInstance().getJedisPool().close();
CoreAPI.getInstance().getRedisClient().close();
simpleLogger.info("已关闭 Redis 连接池"); simpleLogger.info("已关闭 Redis 连接池");
} }
if (CoreAPI.getInstance().isEnableDatabase()) { if (CoreAPI.getInstance().isEnableDatabase()) {

View File

@@ -1,6 +1,9 @@
@file:Suppress("VulnerableLibrariesLocal") @file:Suppress("VulnerableLibrariesLocal")
evaluationDependsOn(":core-common") evaluationDependsOn(":core-common")
evaluationDependsOn(":core-relocate-lettuce")
val shade = configurations.create("shade")
dependencies { dependencies {
api(project(":core-common")) { isTransitive = false } api(project(":core-common")) { isTransitive = false }
@@ -14,13 +17,17 @@ dependencies {
exclude(module = "adventure-api") exclude(module = "adventure-api")
exclude(group = "org.jetbrains") exclude(group = "org.jetbrains")
} }
// https://mvnrepository.com/artifact/redis.clients/jedis
implementation("com.zaxxer:HikariCP:4.0.3") { exclude(group = "org.slf4j") }
api("redis.clients:jedis:5.1.4") { api("redis.clients:jedis:5.1.4") {
exclude(group = "com.google.code.gson") exclude(group = "com.google.code.gson")
exclude(group = "org.slf4j") exclude(group = "org.slf4j")
} }
compileOnlyApi("io.lettuce:lettuce-core:6.7.1.RELEASE") {
implementation("com.zaxxer:HikariCP:4.0.3") { exclude(group = "org.slf4j") } exclude(group = "org.slf4j")
exclude(group = "io.netty")
}
shade(project(":core-relocate-lettuce"))
} }
tasks { tasks {
@@ -33,6 +40,13 @@ tasks {
archiveBaseName = "HamsterCore-BungeeCord" archiveBaseName = "HamsterCore-BungeeCord"
} }
shadowJar { shadowJar {
dependsOn(":core-relocate-lettuce:shadowJar")
val task = project(":core-relocate-lettuce").tasks.shadowJar.get()
from(task.outputs.files.map {
if (it.isDirectory) it else zipTree(
it
)
})
destinationDirectory = rootProject.layout.buildDirectory destinationDirectory = rootProject.layout.buildDirectory
} }
} }

View File

@@ -72,6 +72,7 @@ public class HamsterCorePlugin extends Plugin {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
if (CoreAPI.getInstance().isEnableRedis()) { if (CoreAPI.getInstance().isEnableRedis()) {
CoreAPI.getInstance().getJedisPool().close(); CoreAPI.getInstance().getJedisPool().close();
CoreAPI.getInstance().getRedisClient().close();
simpleLogger.info("已关闭 Redis 连接池"); simpleLogger.info("已关闭 Redis 连接池");
} }
if (CoreAPI.getInstance().isEnableDatabase()) { if (CoreAPI.getInstance().isEnableDatabase()) {

View File

@@ -17,14 +17,18 @@ dependencies {
exclude(group = "com.google.code.gson") exclude(group = "com.google.code.gson")
} }
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
compileOnly("com.zaxxer:HikariCP:4.0.3") { isTransitive = false }
// https://mvnrepository.com/artifact/redis.clients/jedis // https://mvnrepository.com/artifact/redis.clients/jedis
compileOnlyApi("redis.clients:jedis:5.1.4") { compileOnlyApi("redis.clients:jedis:5.1.4") {
exclude(group = "com.google.code.gson") exclude(group = "com.google.code.gson")
exclude(group = "org.slf4j") exclude(group = "org.slf4j")
} }
// https://mvnrepository.com/artifact/io.lettuce/lettuce-core
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP compileOnlyApi("io.lettuce:lettuce-core:6.7.1.RELEASE") {
compileOnly("com.zaxxer:HikariCP:4.0.3") { isTransitive = false } exclude(group = "io.netty")
exclude(group = "org.slf4j")
}
} }
tasks { tasks {

View File

@@ -6,6 +6,9 @@ import cn.hamster3.mc.plugin.core.common.util.CoreUtils;
import cn.hamster3.mc.plugin.core.common.util.SimpleLogger; import cn.hamster3.mc.plugin.core.common.util.SimpleLogger;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
import lombok.Getter; import lombok.Getter;
import net.kyori.adventure.platform.AudienceProvider; import net.kyori.adventure.platform.AudienceProvider;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -41,6 +44,11 @@ public abstract class CoreAPI {
*/ */
@Nullable @Nullable
private JedisPool jedisPool; private JedisPool jedisPool;
/**
* Lettuce Redis 连接池
*/
@Nullable
private RedisClient redisClient;
/** /**
* 公用数据库连接池 * 公用数据库连接池
*/ */
@@ -57,6 +65,7 @@ public abstract class CoreAPI {
if (enableRedis) { if (enableRedis) {
logger.info("正在创建 Redis 连接池"); logger.info("正在创建 Redis 连接池");
jedisPool = new JedisPool(config.getString("redis-url")); jedisPool = new JedisPool(config.getString("redis-url"));
redisClient = RedisClient.create(config.getString("redis-url"));
logger.info("Redis 连接池创建完成"); logger.info("Redis 连接池创建完成");
} else { } else {
logger.info("未启用 Redis 功能"); logger.info("未启用 Redis 功能");
@@ -108,6 +117,24 @@ public abstract class CoreAPI {
throw new IllegalStateException("仓鼠核心未启用 Redis 功能"); throw new IllegalStateException("仓鼠核心未启用 Redis 功能");
} }
@NotNull
public RedisClient getRedisClient() {
if (redisClient != null) {
return redisClient;
}
throw new IllegalStateException("仓鼠核心未启用 Redis 功能");
}
@NotNull
public StatefulRedisConnection<String, String> getRedisConnect() {
return getRedisClient().connect();
}
@NotNull
public StatefulRedisPubSubConnection<String, String> getRedisPubSub() {
return getRedisClient().connectPubSub();
}
@NotNull @NotNull
public abstract SimpleLogger getLogger(); public abstract SimpleLogger getLogger();

View File

@@ -0,0 +1,21 @@
// lettuce-core 需要使用 netty但低版本 bukkit 服务端核心自带的 netty 版本 lettuce 需要的版本有冲突
// 所以必须 shadow 并 relocate 依赖 io.netty才能保证两个版本互不冲突
// 但由于 core-bukkit 同时也 shadow 了 adventure-platform-bukkit且 adventure 内部通过反射调用 netty
// 如果在 core-bukkit 中直接 relocate netty会导致 adventure 的反射调用也指向 relocate 后的 netty
// 此时会导致 shadow 后的 adventure 与服务端中其他同样需要使用 netty 的插件关联时发生冲突
// 例如:服务端上安装了 ViaVersion 时core-bukkit 内置的 adventure 将会与其发生冲突
// 因为 adventure 使用 relocate 之后的类路径ViaVersion 在将类转换成原版 netty 类时,会发生 ClassCastException
// 所以只能先将 lettuce-core 与其需要的 netty 一起打包,并 relocate netty然后再使 core-bukkit 依赖这个打包后的版本
// 才能使得 adventure 与 lettuce-core 正常运行
dependencies {
implementation("io.lettuce:lettuce-core:6.7.1.RELEASE") {
exclude(group = "org.slf4j")
}
}
tasks {
shadowJar {
relocate("io.netty", "cn.hamster3.mc.plugin.core.lib.io.netty")
}
}

View File

@@ -9,17 +9,18 @@ dependencies {
compileOnlyApi("net.kyori:adventure-platform-api:4.3.2") { isTransitive = false } compileOnlyApi("net.kyori:adventure-platform-api:4.3.2") { isTransitive = false }
// https://mvnrepository.com/artifact/redis.clients/jedis implementation("com.zaxxer:HikariCP:5.1.0") { isTransitive = false }
api("redis.clients:jedis:5.1.4") { api("redis.clients:jedis:5.1.4") {
exclude(group = "com.google.code.gson") exclude(group = "com.google.code.gson")
exclude(group = "org.slf4j") exclude(group = "org.slf4j")
} }
api("io.lettuce:lettuce-core:6.7.1.RELEASE") {
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP exclude(group = "io.netty")
implementation("com.zaxxer:HikariCP:5.1.0") { isTransitive = false } exclude(group = "org.slf4j")
}
// https://mvnrepository.com/artifact/com.mysql/mysql-connector-j // https://mvnrepository.com/artifact/com.mysql/mysql-connector-j
runtimeOnly("com.mysql:mysql-connector-j:8.3.0") runtimeOnly("com.mysql:mysql-connector-j:8.4.0")
} }
sourceSets.create("templates") { sourceSets.create("templates") {

View File

@@ -85,6 +85,7 @@ public class HamsterCorePlugin {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
if (CoreAPI.getInstance().isEnableRedis()) { if (CoreAPI.getInstance().isEnableRedis()) {
CoreAPI.getInstance().getJedisPool().close(); CoreAPI.getInstance().getJedisPool().close();
CoreAPI.getInstance().getRedisClient().close();
simpleLogger.info("已关闭 Redis 连接池"); simpleLogger.info("已关闭 Redis 连接池");
} }
if (CoreAPI.getInstance().isEnableDatabase()) { if (CoreAPI.getInstance().isEnableDatabase()) {

View File

@@ -1,8 +1,6 @@
pluginManagement { pluginManagement {
repositories { repositories {
maven { maven("https://maven.airgame.net/maven-public/")
url = uri("https://maven.airgame.net/maven-public/")
}
} }
} }
rootProject.name = "hamster-core" rootProject.name = "hamster-core"
@@ -10,3 +8,4 @@ include("core-common")
include("core-bukkit") include("core-bukkit")
include("core-bungee") include("core-bungee")
include("core-velocity") include("core-velocity")
include("core-relocate-lettuce")