14 Commits
1.3.1 ... 1.3.2

30 changed files with 742 additions and 270 deletions

View File

@@ -26,9 +26,9 @@ repositories {
dependencies { dependencies {
// 对于 Bukkit 插件 // 对于 Bukkit 插件
compileOnly("cn.hamster3.mc.plugin:core-bukkit:1.2.2") compileOnly("cn.hamster3.mc.plugin:core-bukkit:1.3.2")
// 对于 BungeeCord 插件 // 对于 BungeeCord 插件
compileOnly("cn.hamster3.mc.plugin:core-bungee:1.2.2") compileOnly("cn.hamster3.mc.plugin:core-bungee:1.3.2")
} }
``` ```
@@ -54,13 +54,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.2.2</version> <version>1.3.2</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.2.2</version> <version>1.3.2</version>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -1,15 +1,15 @@
plugins { plugins {
id("java") id("java-library")
id("maven-publish") id("maven-publish")
id("com.github.johnrengelman.shadow") version "8+" id("com.github.johnrengelman.shadow") version "8+"
} }
group = "cn.hamster3.mc.plugin" group = "cn.hamster3.mc.plugin"
version = "1.3.1" version = "1.3.2"
subprojects { subprojects {
apply { apply {
plugin("java") plugin("java-library")
plugin("maven-publish") plugin("maven-publish")
plugin("com.github.johnrengelman.shadow") plugin("com.github.johnrengelman.shadow")
} }
@@ -44,34 +44,10 @@ subprojects {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE duplicatesStrategy = DuplicatesStrategy.EXCLUDE
} }
processResources { processResources {
val map = mutableMapOf<String, String>() filesMatching("update.yml") {
map["BUILD_ID"] = System.getenv().getOrDefault("BUILD_ID", "DEV") expand(rootProject.properties)
map["BUILD_NUMBER"] = System.getenv().getOrDefault("BUILD_NUMBER", "DEV")
map["BUILD_DISPLAY_NAME"] = System.getenv().getOrDefault("BUILD_DISPLAY_NAME", "DEV")
map["JOB_URL"] = System.getenv().getOrDefault("JOB_URL", "DEV")
map["BUILD_URL"] = System.getenv().getOrDefault("BUILD_URL", "DEV")
map["GIT_COMMIT"] = System.getenv().getOrDefault("GIT_COMMIT", "DEV")
filesMatching("jenkins.yml") {
expand(map)
} }
} }
jar {
archiveClassifier = "dev"
}
shadowJar {
archiveClassifier = ""
relocate("org.quartz", "cn.hamster3.mc.plugin.core.lib.org.quartz")
relocate("org.terracotta.quartz", "cn.hamster3.mc.plugin.core.lib.org.terracotta.quartz")
relocate("com.zaxxer.hikari", "cn.hamster3.mc.plugin.core.lib.com.zaxxer.hikari")
relocate("redis.clients.jedis", "cn.hamster3.mc.plugin.core.lib.redis.clients.jedis")
relocate("org.json", "cn.hamster3.mc.plugin.core.lib.org.json")
relocate("org.apache.commons.pool2", "cn.hamster3.mc.plugin.core.lib.org.apache.commons.pool2")
relocate("net.kyori", "cn.hamster3.mc.plugin.core.lib.net.kyori")
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")
}
build { build {
dependsOn(shadowJar) dependsOn(shadowJar)
} }

View File

@@ -1,44 +1,74 @@
@file:Suppress("VulnerableLibrariesLocal") @file:Suppress("VulnerableLibrariesLocal")
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
evaluationDependsOn(":core-common") evaluationDependsOn(":core-common")
val shade = configurations.create("shade")
val shadeJava8 = configurations.create("shadeJava8")
dependencies { dependencies {
implementation(project(":core-common")) { isTransitive = false } api(project(":core-common")) { isTransitive = false }
compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT") compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT")
implementation("de.tr7zw:item-nbt-api:2.12.3-SNAPSHOT")
compileOnly("net.milkbowl.vault:VaultAPI:1.7") { isTransitive = false } compileOnly("net.milkbowl.vault:VaultAPI:1.7") { isTransitive = false }
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 }
implementation("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")
} }
implementation("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")
} }
implementation("com.zaxxer:HikariCP:4.0.3") { exclude(group = "org.slf4j") }
// https://mvnrepository.com/artifact/redis.clients/jedis // https://mvnrepository.com/artifact/redis.clients/jedis
implementation("redis.clients:jedis:5.1.2") { api("redis.clients:jedis:5.1.2") {
exclude(group = "com.google.code.gson") exclude(group = "com.google.code.gson")
exclude(group = "org.slf4j") exclude(group = "org.slf4j")
} }
// https://mvnrepository.com/artifact/org.quartz-scheduler/quartz // https://mvnrepository.com/artifact/org.quartz-scheduler/quartz
implementation("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false } api("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false }
// https://www.spigotmc.org/resources/nbt-api.7939/
implementation("de.tr7zw:item-nbt-api:2.12.2")
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
implementation("com.zaxxer:HikariCP:4.0.3") { exclude(group = "org.slf4j") }
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib
shade("org.jetbrains.kotlin:kotlin-stdlib:1.9.23") { exclude(group = "org.jetbrains") }
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib-jdk8
shadeJava8("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.23") { exclude(group = "org.jetbrains") }
} }
tasks { tasks {
processResources { processResources {
filesMatching("plugin.yml") { filesMatching("plugin.yml") {
expand(project.properties) expand(rootProject.properties)
} }
} }
withType<Jar> { withType<Jar> {
archiveBaseName = "HamsterCore-Bukkit" archiveBaseName = "HamsterCore-Bukkit"
} }
shadowJar { shadowJar {
from(shade.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.annotations", "cn.hamster3.mc.plugin.core.lib.de.tr7zw.nbtapi.annotations")
}
val shadowJava8 = register<ShadowJar>("shadowJava8") {
dependsOn(":core-common:build")
archiveClassifier = "Java8"
from(project.configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
from(processResources.get().outputs)
from(jar.get().outputs)
from(shadeJava8.map { if (it.isDirectory) it else zipTree(it) })
destinationDirectory = rootProject.layout.buildDirectory
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")
}
build {
dependsOn(shadowJava8)
} }
} }

View File

@@ -9,25 +9,30 @@ import cn.hamster3.mc.plugin.core.bukkit.hook.PointAPI;
import cn.hamster3.mc.plugin.core.bukkit.hook.VaultAPI; import cn.hamster3.mc.plugin.core.bukkit.hook.VaultAPI;
import cn.hamster3.mc.plugin.core.bukkit.listener.CallbackListener; import cn.hamster3.mc.plugin.core.bukkit.listener.CallbackListener;
import cn.hamster3.mc.plugin.core.bukkit.listener.DebugListener; import cn.hamster3.mc.plugin.core.bukkit.listener.DebugListener;
import cn.hamster3.mc.plugin.core.bukkit.listener.JenkinsUpdateListener;
import cn.hamster3.mc.plugin.core.bukkit.page.handler.PageHandler; import cn.hamster3.mc.plugin.core.bukkit.page.handler.PageHandler;
import cn.hamster3.mc.plugin.core.bukkit.page.listener.PageListener; import cn.hamster3.mc.plugin.core.bukkit.page.listener.PageListener;
import cn.hamster3.mc.plugin.core.bukkit.util.MinecraftVersion; import cn.hamster3.mc.plugin.core.bukkit.util.MinecraftVersion;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI; import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
import cn.hamster3.mc.plugin.core.common.util.UpdateCheckUtils;
import lombok.Getter; import lombok.Getter;
import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.Objects;
import java.util.logging.Logger; import java.util.logging.Logger;
@SuppressWarnings("CallToPrintStackTrace") @SuppressWarnings("CallToPrintStackTrace")
@@ -74,11 +79,11 @@ public class HamsterCorePlugin extends JavaPlugin {
} }
File configFile = new File(dataFolder, "config.yml"); File configFile = new File(dataFolder, "config.yml");
if (!configFile.exists()) { if (!configFile.exists()) {
Files.copy( try (InputStream stream = getResource("config.yml")) {
Objects.requireNonNull(getResource("config.yml")), if (stream != null) {
configFile.toPath(), Files.copy(stream, configFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
StandardCopyOption.REPLACE_EXISTING }
); }
} }
CoreBukkitAPI.init(configFile); CoreBukkitAPI.init(configFile);
logger.info("已初始化 CoreAPI"); logger.info("已初始化 CoreAPI");
@@ -108,13 +113,25 @@ public class HamsterCorePlugin extends JavaPlugin {
logger.info("已注册 CallbackListener"); logger.info("已注册 CallbackListener");
Bukkit.getPluginManager().registerEvents(DebugListener.INSTANCE, this); Bukkit.getPluginManager().registerEvents(DebugListener.INSTANCE, this);
logger.info("已注册 DebugListener"); logger.info("已注册 DebugListener");
Bukkit.getPluginManager().registerEvents(JenkinsUpdateListener.INSTANCE, this);
logger.info("已注册 JenkinsUpdateListener");
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
sync(() -> { sync(() -> {
PointAPI.reloadPlayerPointAPIHook(); PointAPI.reloadPlayerPointAPIHook();
VaultAPI.reloadVaultHook(); VaultAPI.reloadVaultHook();
JenkinsUpdateListener.showUpdate(Bukkit.getConsoleSender()); async(() -> {
for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
try (InputStream stream = plugin.getResource("update.yml")) {
if (stream == null) {
continue;
}
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
YamlConfig config = YamlConfig.load(reader);
UpdateCheckUtils.showUpdate(plugin.getName(), config, Bukkit.getConsoleSender()::sendMessage);
}
} catch (IOException ignored) {
}
}
});
}); });
logger.info("仓鼠核心启动完成,总计耗时 " + time + " ms"); logger.info("仓鼠核心启动完成,总计耗时 " + time + " ms");
} }

View File

@@ -1,73 +0,0 @@
package cn.hamster3.mc.plugin.core.bukkit.listener;
import cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin;
import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
import cn.hamster3.mc.plugin.core.common.util.JenkinsUtils;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.UUID;
public class JenkinsUpdateListener implements Listener {
public static final JenkinsUpdateListener INSTANCE = new JenkinsUpdateListener();
public static HashSet<UUID> SHOWED = new HashSet<>();
private JenkinsUpdateListener() {
}
public static void showUpdate(@NotNull CommandSender sender) {
HamsterCorePlugin.async(() -> {
for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
InputStream resource = plugin.getResource("jenkins.yml");
if (resource == null) {
continue;
}
try (InputStreamReader reader = new InputStreamReader(resource, StandardCharsets.UTF_8)) {
YamlConfig jenkinsConfig = YamlConfig.load(reader);
String jobUrl = jenkinsConfig.getString("JOB_URL");
if (jobUrl == null || jobUrl.equalsIgnoreCase("DEV")) {
continue;
}
String buildNumberString = jenkinsConfig.getString("BUILD_NUMBER");
if (buildNumberString == null || buildNumberString.equalsIgnoreCase("DEV")) {
continue;
}
int lastStableBuild = JenkinsUtils.getLastStableBuild(jobUrl, null, null);
int buildNumber = Integer.parseInt(buildNumberString);
int version = lastStableBuild - buildNumber;
if (version <= 0) {
continue;
}
String pluginName = plugin.getName();
sender.sendMessage(String.format(
"§a检测到插件 %s 有 %d 个版本更新, 下载链接: §n§l%s", pluginName, version, jobUrl
));
} catch (IOException ignored) {
}
}
});
}
@EventHandler(ignoreCancelled = true)
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
if (!player.hasPermission("hamster.core.admin")) {
return;
}
if (!SHOWED.add(player.getUniqueId())) {
return;
}
showUpdate(player);
}
}

View File

@@ -48,15 +48,14 @@ public class PageManager {
YamlConfiguration config = YamlConfiguration.loadConfiguration(pageConfigFile); YamlConfiguration config = YamlConfiguration.loadConfiguration(pageConfigFile);
return new PageConfig(plugin, config); return new PageConfig(plugin, config);
} }
try (InputStream resource = plugin.getResource("pages/" + filename)) { try (InputStream stream = plugin.getResource("pages/" + filename)) {
if (resource == null) { if (stream != null) {
throw new IllegalArgumentException("在插件 " + plugin.getName() + " 的 Jar 文件内部未找到 /pages/" + filename + " "); Files.copy(stream, pageConfigFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} }
Files.copy(resource, pageConfigFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
YamlConfiguration config = YamlConfiguration.loadConfiguration(pageConfigFile);
return new PageConfig(plugin, config);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalArgumentException("为插件 " + pluginName + " 加载页面配置文件 " + filename + " 时出错", e); throw new IllegalArgumentException("为插件 " + pluginName + " 加载页面配置文件 " + filename + " 时出错", e);
} }
YamlConfiguration config = YamlConfiguration.loadConfiguration(pageConfigFile);
return new PageConfig(plugin, config);
} }
} }

View File

@@ -312,10 +312,9 @@ public final class CoreBukkitUtils {
if (!file.exists()) { if (!file.exists()) {
plugin.getLogger().info("生成配置文件: " + filename); plugin.getLogger().info("生成配置文件: " + filename);
try (InputStream stream = plugin.getResource(filename)) { try (InputStream stream = plugin.getResource(filename)) {
if (stream == null) { if (stream != null) {
throw new NullPointerException("在插件 " + plugin.getName() + " 的文件内部未找到 " + filename + " "); Files.copy(stream, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
} }
Files.copy(stream, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalArgumentException("在插件 " + plugin.getName() + " 内部读取文件 " + filename + " 时发生错误"); throw new IllegalArgumentException("在插件 " + plugin.getName() + " 内部读取文件 " + filename + " 时发生错误");
} }

View File

@@ -61,11 +61,13 @@ public class MinecraftVersion {
@NotNull @NotNull
public static Class<?> getNMSClass(@NotNull String className) throws ClassNotFoundException { public static Class<?> getNMSClass(@NotNull String className) throws ClassNotFoundException {
if (Version2 >= 17) {
return Class.forName("net.minecraft.server." + className);
}
String nmsVersion = getNMSVersion(); String nmsVersion = getNMSVersion();
return Class.forName("net.minecraft.server." + nmsVersion + "." + className); return Class.forName("net.minecraft.server." + nmsVersion + "." + className);
} }
@NotNull @NotNull
public static Class<?> getNMSClassSilent(@NotNull String className) { public static Class<?> getNMSClassSilent(@NotNull String className) {
String nmsVersion = getNMSVersion(); String nmsVersion = getNMSVersion();

View File

@@ -1,6 +0,0 @@
BUILD_ID: ${BUILD_ID}
BUILD_NUMBER: ${BUILD_NUMBER}
BUILD_DISPLAY_NAME: ${BUILD_DISPLAY_NAME}
JOB_URL: ${JOB_URL}
BUILD_URL: ${BUILD_URL}
GIT_COMMIT: ${GIT_COMMIT}

View File

@@ -14,9 +14,6 @@ softdepend:
- PlayerPoints - PlayerPoints
- PlaceholderAPI - PlaceholderAPI
loadbefore:
- HamsterAPI
commands: commands:
hamster-core: hamster-core:
aliases: [ hcore ] aliases: [ hcore ]

View File

@@ -0,0 +1,6 @@
version: ${version}
CHECK_TYPE: GITEA_RELEASES
GIT_BASE_URL: https://git.airgame.net
GIT_REPO: MiniDay/hamster-core
GIT_TOKEN: a44a69a4d1b8601bf6091403247759cd28764d5e
DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-core/

View File

@@ -1,28 +1,38 @@
@file:Suppress("VulnerableLibrariesLocal") @file:Suppress("VulnerableLibrariesLocal")
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
evaluationDependsOn(":core-common") evaluationDependsOn(":core-common")
val shade = configurations.create("shade")
val shadeJava8 = configurations.create("shadeJava8")
dependencies { dependencies {
implementation(project(":core-common")) { isTransitive = false } api(project(":core-common")) { isTransitive = false }
compileOnly("net.md-5:bungeecord-api:1.20-R0.1") compileOnly("net.md-5:bungeecord-api:1.20-R0.1")
implementation("net.kyori:adventure-platform-bungeecord:4.3.2") { api("net.kyori:adventure-platform-bungeecord:4.3.2") {
exclude(group = "org.jetbrains") exclude(group = "org.jetbrains")
exclude(group = "com.google.code.gson") exclude(group = "com.google.code.gson")
} }
implementation("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")
} }
implementation("com.zaxxer:HikariCP:4.0.3") { exclude(group = "org.slf4j") }
// https://mvnrepository.com/artifact/redis.clients/jedis // https://mvnrepository.com/artifact/redis.clients/jedis
implementation("redis.clients:jedis:5.1.2") { api("redis.clients:jedis:5.1.2") {
exclude(group = "com.google.code.gson") exclude(group = "com.google.code.gson")
exclude(group = "org.slf4j") exclude(group = "org.slf4j")
} }
// https://mvnrepository.com/artifact/org.quartz-scheduler/quartz // https://mvnrepository.com/artifact/org.quartz-scheduler/quartz
implementation("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false } api("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false }
implementation("com.zaxxer:HikariCP:4.0.3") { exclude(group = "org.slf4j") }
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib
shade("org.jetbrains.kotlin:kotlin-stdlib:1.9.23") { exclude(group = "org.jetbrains") }
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib-jdk8
shadeJava8("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.23") { exclude(group = "org.jetbrains") }
} }
tasks { tasks {
@@ -35,6 +45,23 @@ tasks {
archiveBaseName = "HamsterCore-BungeeCord" archiveBaseName = "HamsterCore-BungeeCord"
} }
shadowJar { shadowJar {
from(shade.map { if (it.isDirectory) it else zipTree(it) })
destinationDirectory = rootProject.layout.buildDirectory destinationDirectory = rootProject.layout.buildDirectory
} }
shadowJar {
from(shade.map { if (it.isDirectory) it else zipTree(it) })
destinationDirectory = rootProject.layout.buildDirectory
}
val shadowJava8 = register<ShadowJar>("shadowJava8") {
dependsOn(":core-common:build")
archiveClassifier = "Java8"
from(project.configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
from(processResources.get().outputs)
from(jar.get().outputs)
from(shadeJava8.map { if (it.isDirectory) it else zipTree(it) })
destinationDirectory = rootProject.layout.buildDirectory
}
build {
dependsOn(shadowJava8)
}
} }

View File

@@ -3,14 +3,12 @@ package cn.hamster3.mc.plugin.core.bungee;
import cn.hamster3.mc.plugin.core.bungee.api.CoreBungeeAPI; import cn.hamster3.mc.plugin.core.bungee.api.CoreBungeeAPI;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI; import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import cn.hamster3.mc.plugin.core.common.config.YamlConfig; import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
import cn.hamster3.mc.plugin.core.common.util.JenkinsUtils; import cn.hamster3.mc.plugin.core.common.util.UpdateCheckUtils;
import lombok.Getter; import lombok.Getter;
import net.kyori.adventure.platform.bungeecord.BungeeAudiences; import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -41,11 +39,11 @@ public class HamsterCorePlugin extends Plugin {
} }
File configFile = new File(dataFolder, "config.yml"); File configFile = new File(dataFolder, "config.yml");
if (!configFile.exists()) { if (!configFile.exists()) {
Files.copy( try (InputStream stream = getResourceAsStream("config.yml")) {
getResourceAsStream("config.yml"), if (stream != null) {
configFile.toPath(), Files.copy(stream, configFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
StandardCopyOption.REPLACE_EXISTING }
); }
} }
CoreBungeeAPI.init(configFile); CoreBungeeAPI.init(configFile);
logger.info("已初始化 CoreAPI"); logger.info("已初始化 CoreAPI");
@@ -66,7 +64,24 @@ public class HamsterCorePlugin extends Plugin {
logger.info("已创建 AudienceProvider"); logger.info("已创建 AudienceProvider");
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
logger.info("仓鼠核心启动完成,总计耗时 " + time + " ms"); logger.info("仓鼠核心启动完成,总计耗时 " + time + " ms");
showUpdate(ProxyServer.getInstance().getConsole());
CoreAPI.getInstance().getExecutorService().submit(() -> {
for (Plugin plugin : ProxyServer.getInstance().getPluginManager().getPlugins()) {
try (InputStream stream = plugin.getResourceAsStream("update.yml")) {
if (stream == null) {
continue;
}
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
YamlConfig config = YamlConfig.load(reader);
UpdateCheckUtils.showUpdate(
plugin.getDescription().getName(), config,
(s) -> ProxyServer.getInstance().getConsole().sendMessage(TextComponent.fromLegacyText(s))
);
}
} catch (IOException ignored) {
}
}
});
} }
@Override @Override
@@ -84,37 +99,4 @@ public class HamsterCorePlugin extends Plugin {
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
logger.info("仓鼠核心已关闭,总计耗时 " + time + " ms"); logger.info("仓鼠核心已关闭,总计耗时 " + time + " ms");
} }
private void showUpdate(@NotNull CommandSender sender) {
ProxyServer.getInstance().getScheduler().runAsync(HamsterCorePlugin.getInstance(), () -> {
for (Plugin plugin : ProxyServer.getInstance().getPluginManager().getPlugins()) {
InputStream resource = plugin.getResourceAsStream("jenkins.yml");
if (resource == null) {
continue;
}
try (InputStreamReader reader = new InputStreamReader(resource, StandardCharsets.UTF_8)) {
YamlConfig jenkinsConfig = YamlConfig.load(reader);
String jobUrl = jenkinsConfig.getString("JOB_URL");
if (jobUrl == null || jobUrl.equalsIgnoreCase("DEV")) {
continue;
}
String buildNumberString = jenkinsConfig.getString("BUILD_NUMBER");
if (buildNumberString == null || buildNumberString.equalsIgnoreCase("DEV")) {
continue;
}
int lastStableBuild = JenkinsUtils.getLastStableBuild(jobUrl, null, null);
int buildNumber = Integer.parseInt(buildNumberString);
int version = lastStableBuild - buildNumber;
if (version <= 0) {
continue;
}
String pluginName = plugin.getDescription().getName();
sender.sendMessage(TextComponent.fromLegacyText(String.format(
"§a检测到插件 %s 落后 %d 个版本更新, 下载链接: §n§l%s", pluginName, version, jobUrl
)));
} catch (IOException ignored) {
}
}
});
}
} }

View File

@@ -22,7 +22,7 @@ public final class CoreBungeeAPI extends CoreAPI {
@NotNull @NotNull
private final Gson gson; private final Gson gson;
@NotNull @NotNull
private final Gson humanGson; private final Gson gsonHuman;
private CoreBungeeAPI(@NotNull ConfigSection config) { private CoreBungeeAPI(@NotNull ConfigSection config) {
super(config); super(config);
@@ -30,7 +30,7 @@ public final class CoreBungeeAPI extends CoreAPI {
.registerTypeAdapter(Component.class, ComponentTypeAdapter.INSTANCE) .registerTypeAdapter(Component.class, ComponentTypeAdapter.INSTANCE)
.registerTypeAdapter(DisplayMessage.class, MessageTypeAdapter.INSTANCE) .registerTypeAdapter(DisplayMessage.class, MessageTypeAdapter.INSTANCE)
.create(); .create();
humanGson = new GsonBuilder() gsonHuman = new GsonBuilder()
.registerTypeAdapter(Component.class, ComponentTypeAdapter.INSTANCE) .registerTypeAdapter(Component.class, ComponentTypeAdapter.INSTANCE)
.registerTypeAdapter(DisplayMessage.class, MessageTypeAdapter.INSTANCE) .registerTypeAdapter(DisplayMessage.class, MessageTypeAdapter.INSTANCE)
.serializeNulls() .serializeNulls()
@@ -62,7 +62,7 @@ public final class CoreBungeeAPI extends CoreAPI {
@Override @Override
public @NotNull Gson getGsonHuman() { public @NotNull Gson getGsonHuman() {
return humanGson; return gsonHuman;
} }
@Override @Override

View File

@@ -8,6 +8,7 @@ import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
@@ -66,11 +67,11 @@ public final class CoreBungeeCordUtils {
} }
File configFile = new File(plugin.getDataFolder(), filename); File configFile = new File(plugin.getDataFolder(), filename);
try { try {
Files.copy( try (InputStream stream = plugin.getResourceAsStream(filename)) {
plugin.getResourceAsStream(filename), if (stream != null) {
configFile.toPath(), Files.copy(stream, configFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
StandardCopyOption.REPLACE_EXISTING }
); }
return ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile); return ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);

View File

@@ -1,6 +0,0 @@
BUILD_ID: ${BUILD_ID}
BUILD_NUMBER: ${BUILD_NUMBER}
BUILD_DISPLAY_NAME: ${BUILD_DISPLAY_NAME}
JOB_URL: ${JOB_URL}
BUILD_URL: ${BUILD_URL}
GIT_COMMIT: ${GIT_COMMIT}

View File

@@ -0,0 +1,6 @@
version: ${version}
CHECK_TYPE: GITEA_RELEASES
GIT_BASE_URL: https://git.airgame.net
GIT_REPO: MiniDay/hamster-core
GIT_TOKEN: a44a69a4d1b8601bf6091403247759cd28764d5e
DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-core/

View File

@@ -4,21 +4,24 @@ dependencies {
compileOnly("com.google.code.gson:gson:2.8.0") compileOnly("com.google.code.gson:gson:2.8.0")
// https://mvnrepository.com/artifact/org.yaml/snakeyaml // https://mvnrepository.com/artifact/org.yaml/snakeyaml
compileOnly("org.yaml:snakeyaml:1.30") compileOnly("org.yaml:snakeyaml:1.30")
// https://mvnrepository.com/artifact/org.slf4j/slf4j-api
compileOnly("org.slf4j:slf4j-api:2.0.12")
implementation("net.kyori:adventure-platform-api:4.3.2") { exclude(group = "org.jetbrains") } compileOnlyApi("net.kyori:adventure-platform-api:4.3.2") { exclude(group = "org.jetbrains") }
implementation("net.kyori:adventure-text-serializer-gson:4.13.1") { compileOnlyApi("net.kyori:adventure-text-serializer-gson:4.13.1") {
exclude(group = "org.jetbrains") exclude(group = "org.jetbrains")
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
implementation("redis.clients:jedis:5.1.2") { compileOnlyApi("redis.clients:jedis:5.1.2") {
exclude(group = "com.google.code.gson") exclude(group = "com.google.code.gson")
exclude(group = "org.slf4j") exclude(group = "org.slf4j")
} }
compileOnly("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false } compileOnlyApi("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false }
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
compileOnly("com.zaxxer:HikariCP:4.0.3") { isTransitive = false }
} }
tasks { tasks {

View File

@@ -2,8 +2,8 @@ package cn.hamster3.mc.plugin.core.common.api;
import cn.hamster3.mc.plugin.core.common.config.ConfigSection; import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
import cn.hamster3.mc.plugin.core.common.thread.NamedThreadFactory; import cn.hamster3.mc.plugin.core.common.thread.NamedThreadFactory;
import cn.hamster3.mc.plugin.core.common.util.CoreUtils;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import lombok.Getter; import lombok.Getter;
import net.kyori.adventure.platform.AudienceProvider; import net.kyori.adventure.platform.AudienceProvider;
@@ -58,22 +58,7 @@ public abstract class CoreAPI {
throw new IllegalArgumentException("配置文件中未找到 datasource 节点"); throw new IllegalArgumentException("配置文件中未找到 datasource 节点");
} }
getLogger().info("正在创建数据库连接池"); getLogger().info("正在创建数据库连接池");
HikariConfig hikariConfig = new HikariConfig(); hikariDataSource = (HikariDataSource) CoreUtils.getDataSource(datasourceConfig);
hikariConfig.setDriverClassName(datasourceConfig.getString("driver"));
hikariConfig.setJdbcUrl(datasourceConfig.getString("url"));
hikariConfig.setUsername(datasourceConfig.getString("username"));
hikariConfig.setPassword(datasourceConfig.getString("password"));
hikariConfig.setMaximumPoolSize(datasourceConfig.getInt("maximum-pool-size", 3));
hikariConfig.setMinimumIdle(datasourceConfig.getInt("minimum-idle", 1));
long keepAliveTime = datasourceConfig.getLong("keep-alive-time", 0);
if (keepAliveTime > 5000) {
hikariConfig.setKeepaliveTime(keepAliveTime);
}
hikariConfig.setIdleTimeout(datasourceConfig.getLong("idle-timeout", 10 * 60 * 1000));
hikariConfig.setMaxLifetime(datasourceConfig.getLong("max-lifetime", 30 * 60 * 1000));
hikariConfig.setValidationTimeout(datasourceConfig.getLong("validation-timeout", 5000));
hikariConfig.setPoolName("HamsterCore-Pool");
hikariDataSource = new HikariDataSource(hikariConfig);
getLogger().info("数据库连接池创建完成"); getLogger().info("数据库连接池创建完成");
} }

View File

@@ -1,12 +1,16 @@
package cn.hamster3.mc.plugin.core.common.util; package cn.hamster3.mc.plugin.core.common.util;
import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import net.kyori.adventure.key.Key; import net.kyori.adventure.key.Key;
import net.kyori.adventure.sound.Sound; import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.title.Title; import net.kyori.adventure.title.Title;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.sql.DataSource;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
@@ -121,6 +125,26 @@ public final class CoreUtils {
); );
} }
@NotNull
public static DataSource getDataSource(@NotNull ConfigSection datasourceConfig) {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName(datasourceConfig.getString("driver"));
hikariConfig.setJdbcUrl(datasourceConfig.getString("url"));
hikariConfig.setUsername(datasourceConfig.getString("username"));
hikariConfig.setPassword(datasourceConfig.getString("password"));
hikariConfig.setMaximumPoolSize(datasourceConfig.getInt("maximum-pool-size", 3));
hikariConfig.setMinimumIdle(datasourceConfig.getInt("minimum-idle", 1));
long keepAliveTime = datasourceConfig.getLong("keep-alive-time", 0);
if (keepAliveTime > 5000) {
hikariConfig.setKeepaliveTime(keepAliveTime);
}
hikariConfig.setIdleTimeout(datasourceConfig.getLong("idle-timeout", 10 * 60 * 1000));
hikariConfig.setMaxLifetime(datasourceConfig.getLong("max-lifetime", 30 * 60 * 1000));
hikariConfig.setValidationTimeout(datasourceConfig.getLong("validation-timeout", 5000));
hikariConfig.setPoolName(datasourceConfig.getString("name", "HamsterCore-Pool"));
return new HikariDataSource(hikariConfig);
}
/** /**
* 按照给定的表达式计算 * 按照给定的表达式计算
* <p> * <p>

View File

@@ -1,41 +0,0 @@
package cn.hamster3.mc.plugin.core.common.util;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public final class JenkinsUtils {
private static final JsonParser JSON_PARSER = new JsonParser();
private JenkinsUtils() {
}
public static int getLastStableBuild(@NotNull String jobUrl, @Nullable String username, @Nullable String apiToken) throws IOException {
URL url = new URL(jobUrl + "api/json");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setRequestMethod("GET");
if (username != null && apiToken != null) {
String token = username + ":" + apiToken;
String base64 = Base64.getEncoder().encodeToString(token.getBytes(StandardCharsets.UTF_8));
connection.setRequestProperty("Authorization", "Basic " + base64);
}
connection.connect();
try (InputStream stream = connection.getInputStream()) {
try (InputStreamReader reader = new InputStreamReader(stream)) {
JsonObject object = JSON_PARSER.parse(reader).getAsJsonObject();
JsonObject lastStableBuild = object.getAsJsonObject("lastStableBuild");
return lastStableBuild.get("number").getAsInt();
}
}
}
}

View File

@@ -0,0 +1,147 @@
package cn.hamster3.mc.plugin.core.common.util;
import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public final class UpdateCheckUtils {
private static final JsonParser JSON_PARSER = new JsonParser();
private UpdateCheckUtils() {
}
public static void showUpdate(@NotNull String pluginName, @NotNull ConfigSection config, @NotNull UpdateReceiver sender) {
String version = config.getString("version");
if (version == null) {
return;
}
String checkType = config.getString("CHECK_TYPE", "");
try {
switch (checkType) {
case "GITEA_RELEASES": {
String baseUrl = config.getString("GIT_BASE_URL");
String gitRepo = config.getString("GIT_REPO");
String downloadUrl = config.getString("DOWNLOAD_URL");
if (baseUrl == null || gitRepo == null || downloadUrl == null) {
return;
}
String gitToken = config.getString("GIT_TOKEN");
String lastRelease = getGiteaLastRelease(baseUrl, gitRepo, gitToken);
if (lastRelease == null) {
break;
}
if (lastRelease.compareToIgnoreCase(version) <= 0) {
break;
}
sender.sendMessage(String.format("§a插件 §l%s§a 发布了新版本 %s", pluginName, lastRelease));
sender.sendMessage(String.format("§b下载链接: §n%s", downloadUrl));
break;
}
case "GITLAB_RELEASES": {
String baseUrl = config.getString("GIT_BASE_URL");
String gitRepo = config.getString("GIT_REPO");
String downloadUrl = config.getString("DOWNLOAD_URL");
if (baseUrl == null || gitRepo == null || downloadUrl == null) {
return;
}
String gitToken = config.getString("GIT_TOKEN");
int projectID = getGitlabProjectID(baseUrl, gitRepo, gitToken);
if (projectID < 0) {
break;
}
String lastRelease = getGitlabLastRelease(baseUrl, projectID, gitToken);
if (lastRelease == null) {
break;
}
if (lastRelease.compareToIgnoreCase(version) <= 0) {
break;
}
sender.sendMessage(String.format("§a插件 §l%s§a 发布了新版本 %s", pluginName, lastRelease));
sender.sendMessage(String.format("§b下载链接: §n%s", downloadUrl));
break;
}
}
} catch (Exception ignored) {
}
}
@Nullable
public static String getGiteaLastRelease(@NotNull String baseUrl, @NotNull String repo, @Nullable String token) throws IOException {
URL url = new URL(baseUrl + "/api/v1/repos/" + repo + "/releases?limit=10");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setRequestMethod("GET");
if (token != null) {
connection.setRequestProperty("Authorization", "token " + token);
}
connection.connect();
try (InputStream stream = connection.getInputStream()) {
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
JsonArray array = JSON_PARSER.parse(reader).getAsJsonArray();
for (JsonElement element : array) {
JsonObject object = element.getAsJsonObject();
//noinspection SpellCheckingInspection
if (object.get("prerelease").getAsBoolean()) {
continue;
}
return object.get("name").getAsString();
}
}
}
return null;
}
public static int getGitlabProjectID(@NotNull String baseUrl, @NotNull String repo, @Nullable String token) throws IOException {
URL url = new URL("https://" + baseUrl + "/api/v4/projects?search=" + repo);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setRequestMethod("GET");
if (token != null) {
connection.setRequestProperty("PRIVATE-TOKEN", "token " + token);
}
connection.connect();
try (InputStream stream = connection.getInputStream()) {
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
JsonArray array = JSON_PARSER.parse(reader).getAsJsonArray();
for (JsonElement element : array) {
JsonObject object = element.getAsJsonObject();
return object.get("id").getAsInt();
}
}
}
return -1;
}
@Nullable
public static String getGitlabLastRelease(@NotNull String baseUrl, int repo, @Nullable String token) throws IOException {
URL url = new URL(baseUrl + "/api/v4/projects/" + repo + "/releases/permalink/latest");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setRequestMethod("GET");
if (token != null) {
connection.setRequestProperty("Authorization", "token " + token);
}
connection.connect();
try (InputStream stream = connection.getInputStream()) {
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
JsonObject object = JSON_PARSER.parse(reader).getAsJsonObject();
return object.get("name").getAsString();
}
}
}
public interface UpdateReceiver {
void sendMessage(@NotNull String message);
}
}

View File

@@ -0,0 +1,57 @@
@file:Suppress("VulnerableLibrariesLocal")
evaluationDependsOn(":core-common")
dependencies {
api(project(":core-common")) { isTransitive = false }
compileOnly("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT")
annotationProcessor("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT")
compileOnlyApi("net.kyori:adventure-platform-api:4.3.2") { isTransitive = false }
// https://mvnrepository.com/artifact/redis.clients/jedis
api("redis.clients:jedis:5.1.2") {
exclude(group = "com.google.code.gson")
exclude(group = "org.slf4j")
}
// https://mvnrepository.com/artifact/org.quartz-scheduler/quartz
api("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false }
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
implementation("com.zaxxer:HikariCP:5.1.0") { isTransitive = false }
// https://mvnrepository.com/artifact/com.mysql/mysql-connector-j
runtimeOnly("com.mysql:mysql-connector-j:8.3.0")
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib
runtimeOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.23") { exclude(group = "org.jetbrains") }
}
sourceSets.create("templates") {
java {
srcDir("src/main/templates")
}
}
val templateSource = file("src/main/templates")
val templateDest = layout.buildDirectory.dir("generated/sources/templates")
val generateTemplates = tasks.register<Copy>("generateTemplates") {
from(templateSource)
into(templateDest)
expand(rootProject.properties)
}
sourceSets.main.get().java.srcDir(generateTemplates.map { it.outputs })
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
withSourcesJar()
}
tasks {
withType<Jar> {
archiveBaseName = "HamsterCore-Velocity"
}
shadowJar {
destinationDirectory = rootProject.layout.buildDirectory
}
}

View File

@@ -0,0 +1,126 @@
package cn.hamster3.mc.plugin.core.velocity;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
import cn.hamster3.mc.plugin.core.common.util.UpdateCheckUtils;
import cn.hamster3.mc.plugin.core.velocity.api.CoreVelocityAPI;
import com.google.inject.Inject;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer;
import com.zaxxer.hikari.HikariDataSource;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import org.slf4j.Logger;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
@Plugin(
id = "hamster-core",
name = "HamsterCore",
version = BuildConstants.VERSION,
description = BuildConstants.DESCRIPTION,
authors = {"MiniDay"}
)
public class HamsterCorePlugin {
@Getter
private static HamsterCorePlugin instance;
@Getter
private final java.util.logging.Logger logger;
@Getter
private final Logger slf4jLogger;
@Getter
private final ProxyServer proxyServer;
@Getter
private final File dataFolder;
@Inject
public HamsterCorePlugin(Logger slf4jLogger, ProxyServer proxyServer, @DataDirectory Path dataPath) {
logger = java.util.logging.Logger.getLogger("hamster-core");
this.slf4jLogger = slf4jLogger;
this.proxyServer = proxyServer;
dataFolder = dataPath.toFile();
instance = this;
long start = System.currentTimeMillis();
try {
if (dataFolder.mkdir()) {
slf4jLogger.info("已生成插件存档文件夹");
}
File configFile = new File(dataFolder, "config.yml");
if (!configFile.exists()) {
try (InputStream stream = HamsterCorePlugin.class.getResourceAsStream("/config.yml")) {
if (stream != null) {
Files.copy(stream, configFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
}
CoreVelocityAPI.init(configFile);
slf4jLogger.info("已初始化 CoreAPI");
} catch (Exception e) {
slf4jLogger.error("初始化 CoreAPI 出错", e);
}
long time = System.currentTimeMillis() - start;
slf4jLogger.info("HamsterCore 初始化完成,总计耗时 " + time + " ms");
}
@Subscribe(order = PostOrder.FIRST)
public void onProxyInitialization(ProxyInitializeEvent event) {
long start = System.currentTimeMillis();
slf4jLogger.info("仓鼠核心正在启动");
long time = System.currentTimeMillis() - start;
slf4jLogger.info("仓鼠核心启动完成,总计耗时 " + time + " ms");
CoreAPI.getInstance().getExecutorService().submit(() -> {
for (PluginContainer plugin : proxyServer.getPluginManager().getPlugins()) {
String pluginName = plugin.getDescription().getName().orElse(null);
if (pluginName == null) {
continue;
}
Object pluginObject = plugin.getInstance().orElse(null);
if (pluginObject == null) {
continue;
}
try (InputStream stream = pluginObject.getClass().getResourceAsStream("/update.yml")) {
if (stream == null) {
continue;
}
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
YamlConfig config = YamlConfig.load(reader);
UpdateCheckUtils.showUpdate(pluginName, config, (s) -> proxyServer.sendMessage(Component.text(s)));
} catch (IOException ignored) {
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
@Subscribe(order = PostOrder.LAST)
public void onProxyShutdown(ProxyShutdownEvent event) {
long start = System.currentTimeMillis();
CoreAPI.getInstance().getJedisPool().close();
slf4jLogger.info("已关闭 Redis 连接池");
if (CoreAPI.getInstance().getDataSource() instanceof HikariDataSource dataSource) {
dataSource.close();
slf4jLogger.info("已关闭数据库连接池");
}
CoreAPI.getInstance().getExecutorService().shutdownNow();
slf4jLogger.info("已关闭 ExecutorService 线程池");
CoreAPI.getInstance().getScheduledService().shutdownNow();
slf4jLogger.info("已关闭 ScheduledExecutorService 线程池");
long time = System.currentTimeMillis() - start;
slf4jLogger.info("HamsterCore 关闭完成,总计耗时 " + time + " ms");
}
}

View File

@@ -0,0 +1,73 @@
package cn.hamster3.mc.plugin.core.velocity.api;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
import cn.hamster3.mc.plugin.core.common.data.DisplayMessage;
import cn.hamster3.mc.plugin.core.common.impl.ComponentTypeAdapter;
import cn.hamster3.mc.plugin.core.common.impl.MessageTypeAdapter;
import cn.hamster3.mc.plugin.core.velocity.HamsterCorePlugin;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import cn.hamster3.mc.plugin.core.velocity.impl.AudienceProviderImpl;
import net.kyori.adventure.platform.AudienceProvider;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;
@SuppressWarnings("unused")
public final class CoreVelocityAPI extends CoreAPI {
@NotNull
private final Gson gson;
@NotNull
private final Gson gsonHuman;
public CoreVelocityAPI(@NotNull ConfigSection config) {
super(config);
gson = new GsonBuilder()
.registerTypeAdapter(Component.class, ComponentTypeAdapter.INSTANCE)
.registerTypeAdapter(DisplayMessage.class, MessageTypeAdapter.INSTANCE)
.create();
gsonHuman = new GsonBuilder()
.registerTypeAdapter(Component.class, ComponentTypeAdapter.INSTANCE)
.registerTypeAdapter(DisplayMessage.class, MessageTypeAdapter.INSTANCE)
.serializeNulls()
.setPrettyPrinting()
.create();
}
public static CoreVelocityAPI getInstance() {
return (CoreVelocityAPI) instance;
}
public static void init(@NotNull File configFile) throws IOException {
if (instance != null) {
return;
}
YamlConfig config = YamlConfig.load(configFile);
instance = new CoreVelocityAPI(config);
}
@Override
public @NotNull AudienceProvider getAudienceProvider() {
return AudienceProviderImpl.INSTANCE;
}
@Override
public @NotNull Logger getLogger() {
return HamsterCorePlugin.getInstance().getLogger();
}
@Override
public @NotNull Gson getGson() {
return gson;
}
@Override
public @NotNull Gson getGsonHuman() {
return gsonHuman;
}
}

View File

@@ -0,0 +1,79 @@
package cn.hamster3.mc.plugin.core.velocity.impl;
import cn.hamster3.mc.plugin.core.velocity.HamsterCorePlugin;
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.platform.AudienceProvider;
import net.kyori.adventure.text.flattener.ComponentFlattener;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
public class AudienceProviderImpl implements AudienceProvider {
public static final AudienceProviderImpl INSTANCE = new AudienceProviderImpl();
private AudienceProviderImpl() {
}
@Override
public @NotNull Audience all() {
return Audience.audience(console(), players());
}
@Override
public @NotNull Audience console() {
return HamsterCorePlugin.getInstance().getProxyServer().getConsoleCommandSource();
}
@Override
public @NotNull Audience players() {
return Audience.audience(HamsterCorePlugin.getInstance().getProxyServer().getAllPlayers());
}
@Override
public @NotNull Audience player(@NotNull UUID playerId) {
Player player = HamsterCorePlugin.getInstance().getProxyServer().getPlayer(playerId).orElse(null);
if (player == null) {
return Audience.empty();
}
return player;
}
@Override
public @NotNull Audience permission(@NotNull String permission) {
return Audience.audience(
HamsterCorePlugin.getInstance().getProxyServer().getAllPlayers()
.stream()
.filter(player -> player.hasPermission(permission))
.toList()
);
}
@Override
public @NotNull Audience world(@NotNull Key world) {
return Audience.empty();
}
@Override
public @NotNull Audience server(@NotNull String serverName) {
return Audience.audience(
HamsterCorePlugin.getInstance().getProxyServer().getAllPlayers()
.stream()
.filter(player -> player.getCurrentServer()
.map(o -> o.getServerInfo().getName().equals(serverName))
.orElse(false)
).toList()
);
}
@Override
public @NotNull ComponentFlattener flattener() {
return ComponentFlattener.basic();
}
@Override
public void close() {
}
}

View File

@@ -0,0 +1,47 @@
# redis 连接配置
# 完整格式如下:
# redis://用户名:密码@主机名:端口/数据库索引?参数名=参数值&参数名=参数值
# 若没有设置 redis 用户名,但设置了密码,则可以使用以下格式:
# redis://:密码@localhost:6379?clientName=HamsterCore
# 若没有设置 redis 用户名,也没有设置密码,则可以使用以下格式:
# redis://localhost:6379?clientName=HamsterCore
# 若不设置数据库,则默认使用 0
redis-url: "redis://localhost:6379/0?clientName=HamsterCore&timeout=5s"
datasource:
# 数据库链接驱动地址
# 除非你知道自己在做什么,否则不建议更改该项
# 旧版服务端低于1.13请使用com.mysql.jdbc.Driver
driver: "com.mysql.cj.jdbc.Driver"
# 数据库链接填写格式:
# jdbc:mysql://{数据库地址}:{数据库端口}/{使用的库名}?参数
# 除非你知道自己在做什么,否则不建议随意更改参数
url: "jdbc:mysql://localhost:3306/Test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true"
# 如果你不需要做多端跨服,那么请使用 sqlite 作本地数据库
# driver: "org.sqlite.JDBC"
# url: "jdbc:sqlite:./plugins/HamsterCore/database.db"
# 用户名
username: "root"
# 密码
password: "Root123.."
# 最小闲置链接数
# 推荐值1~3
minimum-idle: 0
# 最大链接数
# 推荐值不低于3
maximum-pool-size: 3
# 保持连接池可用的间隔
# 除非你的服务器数据库连接经常断开,否则不建议启用该选项
# 单位:毫秒
# 默认值为0禁用
keep-alive-time: 0
# 连接闲置回收时间
# 单位:毫秒
# 推荐值60000010分钟
idle-timeout: 600000
# 链接最长存活时间
# 单位:毫秒
max-lifetime: 1800000
# 验证连接存活的超时时间
# 单位:毫秒
validation-timeout: 5000

View File

@@ -0,0 +1,6 @@
version: ${version}
CHECK_TYPE: GITEA_RELEASES
GIT_BASE_URL: https://git.airgame.net
GIT_REPO: MiniDay/hamster-core
GIT_TOKEN: a44a69a4d1b8601bf6091403247759cd28764d5e
DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-core/

View File

@@ -0,0 +1,8 @@
package cn.hamster3.mc.plugin.core.velocity;
// The constants are replaced before compilation
@SuppressWarnings("unused")
public class BuildConstants {
public static final String VERSION = "${version}";
public static final String DESCRIPTION = "${description}";
}

View File

@@ -9,3 +9,4 @@ rootProject.name = "hamster-core"
include("core-common") include("core-common")
include("core-bukkit") include("core-bukkit")
include("core-bungee") include("core-bungee")
include("core-velocity")