异步工具

异步函数通过BukkitScheduler提交

fixup! 异步工具 异步函数通过BukkitScheduler提交

fixup! 异步工具 异步函数通过BukkitScheduler提交
This commit is contained in:
2026-06-07 18:49:16 +08:00
parent 146a7a602f
commit c67023c913
3 changed files with 189 additions and 26 deletions
@@ -4,24 +4,38 @@ 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
import java.io.File
class Helper : JavaPlugin() { class Helper : JavaPlugin() {
private lateinit var adventure: BukkitAudiences private lateinit var adventure: BukkitAudiences
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)
logger.info("Registering listeners...")
// init data-folders
logger.info("Initializing data-folders...")
File(this.dataFolder, "block-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...")
Bukkit.getPluginManager().registerEvents(ForternListener(this), this) Bukkit.getPluginManager().registerEvents(ForternListener(this), this)
logger.info("Registering commands...")
// register commands // register commands
Bukkit.getPluginCommand("helper")?.setExecutor(HelperCommand(this, adventure)) logger.info("Registering commands...")
Bukkit.getPluginCommand("helper")!!.setExecutor(HelperCommand(this, adventure, asyncManager))
// register placeholders // register placeholders
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
expansion = ForternExpansion(this) expansion = ForternExpansion(this)
@@ -0,0 +1,88 @@
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,14 +11,16 @@ 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("Minehunt v${plugin.description.version}", NamedTextColor.GREEN), Component.text("Minehunt v${plugin.description.version}", NamedTextColor.GREEN),
@@ -97,16 +99,48 @@ 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
var fileExists = true
var parse = true
asyncManager.execInMainAfterAsync("read item nbt in helper command", 20, run@{
if (isNbt) {
val file = File(itemNbtDir, "${i}.nbt") val file = File(itemNbtDir, "${i}.nbt")
if (!file.exists()) return if (!file.exists()) {
NBT.readFile(file) fileExists = false
return@run
}
try {
readWriteNBT = NBT.readFile(file)
} catch (ex: Exception) {
plugin.logger.log(Level.WARNING, "Error reading nbt", ex)
parse = false
}
} else { } else {
val file = File(itemNbtDir, "${i}.txt") val file = File(itemNbtDir, "${i}.txt")
if (!file.exists()) return if (!file.exists()) {
NBT.parseNBT(FileReader(file).readAllAsString()) 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
} }
@@ -120,19 +154,46 @@ 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 itemNbtDir = File(plugin.dataFolder, "block-nbt") val blockNbtDir = File(plugin.dataFolder, "block-nbt")
val readWriteNBT = if (isNbt) {
val file = File(itemNbtDir, "${i}.nbt") var readWriteNBT: ReadWriteNBT? = null
if (!file.exists()) return var fileExists = true
NBT.readFile(file) var parse = true
} else {
val file = File(itemNbtDir, "${i}.txt") asyncManager.execInMainAfterAsync("read block nbt in helper command", 20, run@{
if (!file.exists()) return if (isNbt) {
NBT.parseNBT(FileReader(file).readAllAsString()) val file = File(blockNbtDir, "${i}.nbt")
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(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.modify(blockState) { nbt: ReadWriteNBT ->
nbt.mergeCompound(readWriteNBT) nbt.mergeCompound(readWriteNBT)
} }
}
}
}
return return
} }