Compare commits
11 Commits
03e51b2955
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ed6b8ec5a | |||
| d850455b98 | |||
| 9c3e597789 | |||
| 285f5273c3 | |||
| 8582161da3 | |||
| de3a83deaa | |||
| 9562519d1a | |||
| 29e5216c8c | |||
| 4cf440b843 | |||
| 9d5df29afa | |||
| 50c5c258fe |
@@ -3,15 +3,17 @@ 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@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- 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
|
||||||
@@ -28,8 +30,20 @@ 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
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>xyz.fortern</groupId>
|
<groupId>xyz.fortern</groupId>
|
||||||
<artifactId>fortern-helper</artifactId>
|
<artifactId>fortern-helper</artifactId>
|
||||||
<version>1.2</version>
|
<version>1.3</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.3.21</kotlin.version>
|
<kotlin.version>2.4.0</kotlin.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ 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
|
||||||
@@ -14,6 +15,8 @@ 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)
|
||||||
@@ -23,15 +26,19 @@ 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))
|
Bukkit.getPluginCommand("helper")!!.setExecutor(HelperCommand(this, adventure, asyncManager))
|
||||||
// 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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,17 +11,19 @@ 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("fortern-helper 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)
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,3 +16,8 @@ libraries:
|
|||||||
commands:
|
commands:
|
||||||
helper:
|
helper:
|
||||||
description: "游戏主命令"
|
description: "游戏主命令"
|
||||||
|
permission: fortern.helper
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
fortern.helper:
|
||||||
|
default: op
|
||||||
|
|||||||
Reference in New Issue
Block a user