Compare commits

..

8 Commits

Author SHA1 Message Date
Fortern 03e51b2955 ver 1.2 2026-06-07 11:34:49 +08:00
Fortern febbe3a506 workflows 2026-06-07 11:34:49 +08:00
Fortern 5b511242cc Depending on NBT-API 2026-06-07 10:49:42 +08:00
Fortern 2c38923b65 ver 1.1 2026-06-04 06:18:05 +08:00
Fortern a5a9f8c393 占位符 2026-06-04 06:18:05 +08:00
Fortern 7c4dbf6727 区块等级 2026-06-04 01:34:42 +08:00
Fortern 47977d9be2 村民无限补货 2026-06-04 01:34:42 +08:00
Fortern e78badda3c init 2026-06-04 01:34:42 +08:00
6 changed files with 31 additions and 206 deletions
+2 -16
View File
@@ -3,17 +3,15 @@ run-name: Building Plugin 🚀
on: on:
push: push:
tags: tags:
- 'ver/**' - '*'
jobs: jobs:
build: build:
name: Build with Maven name: Build with Maven
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v6 uses: actions/checkout@v5
- name: Set up JDK 25 and enable Maven cache - name: Set up JDK 25 and enable Maven cache
uses: actions/setup-java@v5 uses: actions/setup-java@v5
@@ -30,20 +28,8 @@ jobs:
- name: Build and package with Maven - name: Build and package with Maven
run: mvn -B package run: mvn -B package
- name: Determine version
id: set-version
shell: bash
run: |
VERSION=${GITHUB_REF#refs/tags/ver/}
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Upload built artifacts - name: Upload built artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: maven-artifacts name: maven-artifacts
path: target/spigot/*.jar path: target/spigot/*.jar
- uses: akkuman/gitea-release-action@v1
with:
name: '${{ steps.set-version.outputs.version }} Release'
files: target/spigot/*.jar
+2 -2
View File
@@ -6,7 +6,7 @@
<groupId>xyz.fortern</groupId> <groupId>xyz.fortern</groupId>
<artifactId>fortern-helper</artifactId> <artifactId>fortern-helper</artifactId>
<version>1.3</version> <version>1.2</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>fortern-helper</name> <name>fortern-helper</name>
@@ -29,7 +29,7 @@
<properties> <properties>
<java.version>25</java.version> <java.version>25</java.version>
<kotlin.version>2.4.0</kotlin.version> <kotlin.version>2.3.21</kotlin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
@@ -4,7 +4,6 @@ import me.clip.placeholderapi.expansion.PlaceholderExpansion
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.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
import xyz.fortern.forternhelper.async.AsyncManager
import xyz.fortern.forternhelper.command.HelperCommand import xyz.fortern.forternhelper.command.HelperCommand
import xyz.fortern.forternhelper.listener.ForternListener import xyz.fortern.forternhelper.listener.ForternListener
import xyz.fortern.forternhelper.placeholder.ForternExpansion import xyz.fortern.forternhelper.placeholder.ForternExpansion
@@ -15,8 +14,6 @@ class Helper : JavaPlugin() {
private lateinit var expansion: PlaceholderExpansion private lateinit var expansion: PlaceholderExpansion
private lateinit var asyncManager: AsyncManager
override fun onEnable() { override fun onEnable() {
// Plugin startup logic // Plugin startup logic
this.adventure = BukkitAudiences.create(this) this.adventure = BukkitAudiences.create(this)
@@ -26,19 +23,15 @@ class Helper : JavaPlugin() {
File(this.dataFolder, "block-nbt").mkdirs() File(this.dataFolder, "block-nbt").mkdirs()
File(this.dataFolder, "item-nbt").mkdirs() File(this.dataFolder, "item-nbt").mkdirs()
// register asyncManager
logger.info("Registering asyncManager...")
asyncManager = AsyncManager(this)
// register listeners // register listeners
logger.info("Registering listeners...") logger.info("Registering listeners...")
Bukkit.getPluginManager().registerEvents(ForternListener(this), this) Bukkit.getPluginManager().registerEvents(ForternListener(this), this)
// register commands // register commands
logger.info("Registering commands...") logger.info("Registering commands...")
Bukkit.getPluginCommand("helper")!!.setExecutor(HelperCommand(this, adventure, asyncManager)) Bukkit.getPluginCommand("helper")?.setExecutor(HelperCommand(this, adventure))
// register placeholders // register placeholders
expansion = ForternExpansion(this)
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
expansion = ForternExpansion(this)
logger.info("Registering placeholders...") logger.info("Registering placeholders...")
expansion.register() expansion.register()
} }
@@ -1,88 +0,0 @@
package xyz.fortern.forternhelper.async
import org.bukkit.Bukkit
import org.bukkit.plugin.java.JavaPlugin
import java.util.concurrent.CompletableFuture
import java.util.logging.Level
class AsyncManager(
private val plugin: JavaPlugin,
) {
private val tasks: MutableSet<AsyncTask> = HashSet()
init {
// sync
Bukkit.getScheduler().runTaskTimer(plugin, Runnable {
val iterator = tasks.iterator()
while (iterator.hasNext()) {
val asyncTaskTracker = iterator.next()
try {
val result = asyncTaskTracker.tick()
if (result == AsyncTask.Result.RUNNING) {
continue
} else if (result == AsyncTask.Result.TIMEOUT) {
plugin.logger.warning("Async task timed out: ${asyncTaskTracker.info}")
}
} catch (t: Throwable) {
plugin.logger.log(Level.SEVERE, "Error while ticking AsyncManager", t)
}
iterator.remove()
}
}, 0, 0)
}
/**
* 添加一个异步任务[asyncFun],该异步任务结束后,会在主线程同步执行另一个任务[syncFun]。
* 需要超时时间[timeout]。
* 此方法应当在主线程调用。
*/
fun execInMainAfterAsync(info: String, timeout: Int, asyncFun: () -> Unit, syncFun: () -> Unit) {
val future = CompletableFuture<Void>()
val bukkitTask = Bukkit.getScheduler().runTaskAsynchronously(plugin, Runnable {
try {
asyncFun()
future.complete(null)
} catch (t: Throwable) {
future.completeExceptionally(t)
}
})
val asyncTaskTracker = AsyncTask(bukkitTask.taskId, info, future, timeout, syncFun)
tasks.add(asyncTaskTracker)
}
}
class AsyncTask(
val id: Int,
val info: String,
val future: CompletableFuture<Void>,
var timeout: Int,
val syncFun: () -> Unit,
) {
/**
* 每tick执行一次。 返回 [Result.TIMEOUT] 如果超时; 返回 [Result.TIMEOUT] 如果正在运行; 返回 [Result.DONE] 如果完成。
*
* @throws Throwable 当异步任务或同步任务执行出现异常时,抛出那个异常
*/
@Throws(Throwable::class)
fun tick(): Result {
if (future.isDone) {
if (!future.isCompletedExceptionally) {
syncFun.invoke()
} else {
throw future.exceptionNow()
}
return Result.DONE
}
timeout--
if (timeout < 0) {
future.cancel(true)
return Result.TIMEOUT
}
return Result.RUNNING
}
enum class Result {
RUNNING, DONE, TIMEOUT
}
}
@@ -11,19 +11,17 @@ import org.bukkit.command.CommandSender
import org.bukkit.command.TabExecutor import org.bukkit.command.TabExecutor
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
import xyz.fortern.forternhelper.async.AsyncManager
import java.io.File import java.io.File
import java.io.FileReader import java.io.FileReader
import java.util.logging.Level
class HelperCommand( class HelperCommand(
private val plugin: JavaPlugin, private val plugin: JavaPlugin,
private val adventure: BukkitAudiences, private val adventure: BukkitAudiences,
private val asyncManager: AsyncManager,
) : TabExecutor { ) : TabExecutor {
private val subCommands: List<String> = listOf("loadlevel") private val subCommands: List<String> = listOf("loadlevel")
private val helpMessages = listOf( private val helpMessages = listOf(
Component.text("fortern-helper v${plugin.description.version}", NamedTextColor.GREEN), Component.text("Minehunt v${plugin.description.version}", NamedTextColor.GREEN),
Component.text("/helper help ", NamedTextColor.GOLD) Component.text("/helper help ", NamedTextColor.GOLD)
.append(Component.text("帮助信息", NamedTextColor.WHITE)), .append(Component.text("帮助信息", NamedTextColor.WHITE)),
Component.text("/helper loadlevel <chunkPosX> <chunkPosZ> [world] ", NamedTextColor.GOLD) Component.text("/helper loadlevel <chunkPosX> <chunkPosZ> [world] ", NamedTextColor.GOLD)
@@ -99,48 +97,16 @@ class HelperCommand(
val i = args[1] val i = args[1]
val isNbt = if (args.size > 2) args[2] == "nbt" else false val isNbt = if (args.size > 2) args[2] == "nbt" else false
val itemNbtDir = File(plugin.dataFolder, "item-nbt") val itemNbtDir = File(plugin.dataFolder, "item-nbt")
val nbt = if (isNbt) {
var readWriteNBT: ReadWriteNBT? = null val file = File(itemNbtDir, "${i}.nbt")
var fileExists = true if (!file.exists()) return
var parse = true NBT.readFile(file)
} else {
asyncManager.execInMainAfterAsync("read item nbt in helper command", 20, run@{ val file = File(itemNbtDir, "${i}.txt")
if (isNbt) { if (!file.exists()) return
val file = File(itemNbtDir, "${i}.nbt") NBT.parseNBT(FileReader(file).readAllAsString())
if (!file.exists()) {
fileExists = false
return@run
}
try {
readWriteNBT = NBT.readFile(file)
} catch (ex: Exception) {
plugin.logger.log(Level.WARNING, "Error reading nbt", ex)
parse = false
}
} else {
val file = File(itemNbtDir, "${i}.txt")
if (!file.exists()) {
fileExists = false
return@run
}
try {
readWriteNBT = NBT.parseNBT(FileReader(file).readAllAsString())
} catch (ex: Exception) {
plugin.logger.log(Level.WARNING, "Error reading nbt", ex)
parse = false
}
}
}) {
if (!fileExists) {
adventure.sender(sender).sendMessage(Component.text("文件不存在"))
} else if (!parse) {
adventure.sender(sender).sendMessage(Component.text("文件解析错误"))
} else {
if (readWriteNBT != null) {
sender.inventory.addItem(NBT.itemStackFromNBT(readWriteNBT))
}
}
} }
sender.inventory.addItem(NBT.itemStackFromNBT(nbt))
return return
} }
@@ -154,45 +120,18 @@ class HelperCommand(
val isNbt = if (args.size > 2) args[2] == "nbt" else false val isNbt = if (args.size > 2) args[2] == "nbt" else false
val world = sender.world val world = sender.world
val blockState = world.getBlockState(0, 128, 0) val blockState = world.getBlockState(0, 128, 0)
val blockNbtDir = File(plugin.dataFolder, "block-nbt") val itemNbtDir = File(plugin.dataFolder, "block-nbt")
val readWriteNBT = if (isNbt) {
var readWriteNBT: ReadWriteNBT? = null val file = File(itemNbtDir, "${i}.nbt")
var fileExists = true if (!file.exists()) return
var parse = true NBT.readFile(file)
} else {
asyncManager.execInMainAfterAsync("read block nbt in helper command", 20, run@{ val file = File(itemNbtDir, "${i}.txt")
if (isNbt) { if (!file.exists()) return
val file = File(blockNbtDir, "${i}.nbt") NBT.parseNBT(FileReader(file).readAllAsString())
if (!file.exists()) { }
fileExists = false NBT.modify(blockState) { nbt: ReadWriteNBT ->
return@run nbt.mergeCompound(readWriteNBT)
}
try {
readWriteNBT = NBT.readFile(file)
} catch (ex: Exception) {
plugin.logger.log(Level.WARNING, "Error reading nbt", ex)
parse = false
}
} else {
val file = File(blockNbtDir, "${i}.txt")
if (!file.exists()) {
fileExists = false
return@run
}
readWriteNBT = NBT.parseNBT(FileReader(file).readAllAsString())
}
}) {
if (!fileExists) {
adventure.sender(sender).sendMessage(Component.text("文件不存在"))
} else if (!parse) {
adventure.sender(sender).sendMessage(Component.text("文件解析错误"))
} else {
if (readWriteNBT != null) {
NBT.modify(blockState) { nbt: ReadWriteNBT ->
nbt.mergeCompound(readWriteNBT)
}
}
}
} }
return return
} }
-5
View File
@@ -16,8 +16,3 @@ libraries:
commands: commands:
helper: helper:
description: "游戏主命令" description: "游戏主命令"
permission: fortern.helper
permissions:
fortern.helper:
default: op