diff --git a/README.md b/README.md index 3116b15..22bb8f9 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,5 @@ -# TabooLib SDK +# Jarnbjorn -## 构建发行版本 - -发行版本用于正常使用, 不含 TabooLib 本体。 - -``` -./gradlew clean build -``` - -## 构建开发版本 - -开发版本包含 TabooLib 本体, 用于开发者使用, 但不可运行。 - -``` -./gradlew clean taboolibBuildApi -PDeleteCode -``` - -> 参数 -PDeleteCode 表示移除所有逻辑代码以减少体积。 \ No newline at end of file +> Jarnbjorn was the Dwarven-forged battle axe composed of Asgardian Steel and created for Rūna, one of the original members of the Valkyrior. After she mysteriously disappeared, having been assimilated by the Headless Celestial, it was stored in Asgard's armory and eventually taken up by Thor long before he obtained Mjolnir. + +你可以简称它为 —— JB。 \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index dc4614a..856b084 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,4 @@ -import io.izzel.taboolib.gradle.BUKKIT -import io.izzel.taboolib.gradle.UNIVERSAL +import io.izzel.taboolib.gradle.* import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { @@ -11,9 +10,9 @@ plugins { taboolib { env { // 安装模块 - install(UNIVERSAL, BUKKIT) + install(UNIVERSAL, BUKKIT_ALL, EXPANSION_JEXL, EFFECT, NMS_UTIL) } - version { taboolib = "6.1.0" } + version { taboolib = "6.1.1-beta3" } } repositories { @@ -21,6 +20,7 @@ repositories { } dependencies { + taboo("com.flowpowered:flow-math:1.0.3") compileOnly("ink.ptms.core:v12004:12004:mapped") compileOnly("ink.ptms.core:v12004:12004:universal") compileOnly(kotlin("stdlib")) diff --git a/gradle.properties b/gradle.properties index d651e29..a4a18cf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -group=io.github.username.project +group=me.asgard.frontier.jarnbjorn version=1.0.0 kotlin.incremental=true kotlin.incremental.java=true diff --git a/libs/Adyeshach-2.0.4-api.jar b/libs/Adyeshach-2.0.4-api.jar new file mode 100644 index 0000000..c04301c Binary files /dev/null and b/libs/Adyeshach-2.0.4-api.jar differ diff --git a/settings.gradle.kts b/settings.gradle.kts index b538bba..40278b7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1 +1 @@ -rootProject.name = "ExamplePlugin" \ No newline at end of file +rootProject.name = "Jarnbjorn" \ No newline at end of file diff --git a/src/main/kotlin/io/github/username/project/ExamplePlugin.kt b/src/main/kotlin/io/github/username/project/ExamplePlugin.kt deleted file mode 100644 index b20d0a1..0000000 --- a/src/main/kotlin/io/github/username/project/ExamplePlugin.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.username.project - -import taboolib.common.platform.Plugin -import taboolib.common.platform.function.info - -object ExamplePlugin : Plugin() { - - override fun onEnable() { - info("Successfully running ExamplePlugin!") - } -} \ No newline at end of file diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/Jarnbjorn.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/Jarnbjorn.kt new file mode 100644 index 0000000..c80fc30 --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/Jarnbjorn.kt @@ -0,0 +1,38 @@ +package me.asgard.frontier.jarnbjorn + +import me.asgard.frontier.jarnbjorn.api.event.PluginReloadEvent +import me.asgard.frontier.jarnbjorn.api.template.BladeTemplateManager +import taboolib.common.LifeCycle +import taboolib.common.platform.Awake +import taboolib.common.platform.Plugin +import taboolib.module.configuration.Config +import taboolib.module.configuration.Configuration + +object Jarnbjorn : Plugin() { + + @Config + lateinit var conf: Configuration + private set + + /** 模板管理器 */ + lateinit var bladeTemplateManager: BladeTemplateManager + + /** + * 重载插件 + */ + @Awake(LifeCycle.ACTIVE) + fun reload() { + // 重载前事件 + PluginReloadEvent.Pre().call() + conf.reload() + bladeTemplateManager.reload() + // 重载后事件 + PluginReloadEvent.Post().call() + } + + fun isBladeTemplateManagerInitialized(): Boolean { + return this::bladeTemplateManager.isInitialized + } +} + +typealias FrontierCombat = Jarnbjorn \ No newline at end of file diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/Permission.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/Permission.kt new file mode 100644 index 0000000..c4dc8e0 --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/Permission.kt @@ -0,0 +1,45 @@ +package me.asgard.frontier.jarnbjorn.api + +import me.asgard.frontier.jarnbjorn.Jarnbjorn +import org.bukkit.entity.Player +import org.bukkit.permissions.PermissionAttachment +import taboolib.platform.util.bukkitPlugin + +fun isPreventAntiCheat(): Boolean { + return Jarnbjorn.conf.getBoolean("prevent-anti-cheat.enable") +} + +fun getPreventAntiCheatPermissions(): List { + return Jarnbjorn.conf.getStringList("prevent-anti-cheat.permission") +} + +/** + * 临时赋予玩家绕过反作弊的权限 + * 防止在一些特殊的伤害动作下被反作弊系统拦截 + */ +fun preventAntiCheat(player: Player, runnable: () -> T?): T? { + var r: T? = null + grantPermission(player).run { + try { + r = runnable() + } catch (t: Throwable) { + t.printStackTrace() + } + revokePermission(player, this) + } + return r +} + +fun grantPermission(player: Player): PermissionAttachment { + val attachment = player.addAttachment(bukkitPlugin) + if (isPreventAntiCheat()) { + getPreventAntiCheatPermissions().forEach { + attachment.setPermission(it, true) + } + } + return attachment +} + +fun revokePermission(player: Player, attachment: PermissionAttachment) { + player.removeAttachment(attachment) +} \ No newline at end of file diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/blade/BladePainter.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/blade/BladePainter.kt new file mode 100644 index 0000000..389ed82 --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/blade/BladePainter.kt @@ -0,0 +1,129 @@ +package me.asgard.frontier.jarnbjorn.api.blade + +import ink.ptms.adyeshach.core.util.modify +import ink.ptms.adyeshach.core.util.plus +import me.asgard.frontier.jarnbjorn.api.template.BladeTemplate +import org.bukkit.Location +import org.bukkit.Particle +import org.bukkit.entity.Player +import taboolib.common.platform.function.info +import taboolib.common.platform.function.submitAsync +import taboolib.module.effect.createLine +import taboolib.module.effect.createNRankBezierCurve +import taboolib.module.nms.createPacket +import taboolib.module.nms.sendBundlePacket +import taboolib.module.nms.sendBundlePacketBlocking +import taboolib.module.nms.sendPacket +import taboolib.platform.util.onlinePlayers +import taboolib.platform.util.toBukkitLocation +import taboolib.platform.util.toProxyLocation + +/** + * Jarnbjorn + * me.asgard.frontier.jarnbjorn.api.blade.BladePainter + * + * @author 坏黑 + * @since 2024/3/3 17:50 + */ +class BladePainter(val template: BladeTemplate) { + + /** 基础位置 */ + private val base = template.location.add(0.5, 2.0, 0.5).modify(0f, 0f) + + /** 中心点 */ + private val mid = template.startPoint.toVector().getMidpoint(template.endPoint.toVector()) + + /** 路径 */ + private val path = Path(template.startPoint.toVector(), template.endPoint.toVector(), mid) + + /** 旋转器 */ + private val rotator = Rotator(base.toVector(), base.yaw, base.pitch, path) + + /** 当前视角 */ + private var eyeLocation = base + get() = field.clone() + + private var distance = 1.5 + private var step = 0.05 + private var points = arrayListOf() + private var packets = arrayListOf() + private var groupSize = -1 + + /** 设置坐标 */ + fun use(eyeLocation: Location): BladePainter { + this.eyeLocation = eyeLocation + return this + } + + /** 设置距离 */ + fun distance(distance: Double): BladePainter { + this.distance = distance + return this + } + + /** 设置步长 */ + fun step(step: Double): BladePainter { + this.step += step + return this + } + + /** 绘制曲线 */ + fun draw(): BladePainter { + val rotated = rotator.rotate(eyeLocation) + val start = rotated.getStartLocation(base.world!!) + val end = rotated.getEndLocation(base.world!!) + val mid = start.toVector().getMidpoint(end.toVector()) + val midLoc = mid.clone().add(mid.clone().subtract(eyeLocation.toVector()).normalize().multiply(distance)).toLocation(base.world!!) + points.clear() + createNRankBezierCurve(listOf(start.toProxyLocation(), midLoc.toProxyLocation(), end.toProxyLocation()), step) { points += it.toBukkitLocation() }.show() + info("draw ${points.size} points") + return this + } + + /** 生成粒子包 */ + fun build(func: (index: Int, loc: Location) -> Any): BladePainter { + packets.clear() + points.forEachIndexed { index, loc -> packets += func(index, loc) } + return this + } + + /** 每组大小 */ + fun groupSize(groupSize: Int): BladePainter { + this.groupSize = groupSize + return this + } + + /** 发送粒子 */ + fun sendTo(player: Player, stayTicks: Int, internal: Long): BladePainter { + submitAsync { + packets.split(groupSize) { _, p -> + player.sendBundlePacketBlocking(p) + Thread.sleep(internal) + } + repeat(stayTicks) { + player.sendBundlePacketBlocking(packets) + Thread.sleep(50) + } + } + return this + } + + /** + * 将列表分割为多个列表 + */ + internal fun List.split(groupSize: Int, func: (i: Int, el: List) -> Unit) { + if (groupSize == -1) { + func(0, this) + return + } + val size = size + val group = size / groupSize + val remainder = size % groupSize + for (i in 0 until group) { + func(i, subList(i * groupSize, (i + 1) * groupSize)) + } + if (remainder != 0) { + func(group, subList(group * groupSize, size)) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/blade/Direction.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/blade/Direction.kt new file mode 100644 index 0000000..0111e3d --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/blade/Direction.kt @@ -0,0 +1,101 @@ +package me.asgard.frontier.jarnbjorn.api.blade + +import org.bukkit.util.Vector +import kotlin.math.absoluteValue + +/** + * FrontierCombat + * me.asgard.frontier.combat.util.Direction + * + * @author 坏黑 + * @since 2023/5/28 21:30 + */ +enum class Direction { + + /** 自上而下 **/ + UP, + + /** 自下而上 **/ + DOWN, + + /** 自左而右 **/ + LEFT, + + /** 自右而左 **/ + RIGHT, + + /** 左上到右下 **/ + LEFT_UP, + + /** 右上到左下 **/ + RIGHT_UP, + + /** 左下到右上 **/ + LEFT_DOWN, + + /** 右下到左上 **/ + RIGHT_DOWN; + + /** 获取反向 **/ + fun opposite(): Direction { + return when (this) { + UP -> DOWN + DOWN -> UP + LEFT -> RIGHT + RIGHT -> LEFT + LEFT_UP -> RIGHT_DOWN + RIGHT_UP -> LEFT_DOWN + LEFT_DOWN -> RIGHT_UP + RIGHT_DOWN -> LEFT_UP + } + } + + companion object { + + /** + * 从字符串获取方向 + */ + fun fromString(str: String): Direction? { + return when (str) { + "UP" -> UP + "DOWN" -> DOWN + "LEFT" -> LEFT + "RIGHT" -> RIGHT + "LEFT_UP" -> LEFT_UP + "RIGHT_UP" -> RIGHT_UP + "LEFT_DOWN" -> LEFT_DOWN + "RIGHT_DOWN" -> RIGHT_DOWN + else -> null + } + } + + /** + * 获取方向 + */ + fun get(start: Vector, end: Vector, center: Vector): Direction { + // 计算差值 + val diff = end.clone().subtract(start).normalize() + // 是否自上而下 + val isUp = diff.y < 0 + // 是否自左到右 + val isLeft = (end.x - start.x) * (center.z - start.z) - (center.x - start.x) * (end.z - start.z) > 0 + + // 是否垂直(差值 0.45) + val isVertical = diff.x.absoluteValue < 0.45 && diff.z.absoluteValue < 0.45 + // 是否水平 + val isHorizontal = diff.y.absoluteValue < 0.45 + + // 计算方向 + return when { + isVertical && isUp -> UP + isVertical && !isUp -> DOWN + isHorizontal && isLeft -> LEFT + isHorizontal && !isLeft -> RIGHT + isUp && isLeft -> LEFT_UP + isUp && !isLeft -> RIGHT_UP + !isUp && isLeft -> LEFT_DOWN + else -> RIGHT_DOWN + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/blade/Path.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/blade/Path.kt new file mode 100644 index 0000000..b932413 --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/blade/Path.kt @@ -0,0 +1,28 @@ +package me.asgard.frontier.jarnbjorn.api.blade + +import org.bukkit.Location +import org.bukkit.World +import org.bukkit.util.Vector + +/** + * FrontierCombat + * me.asgard.frontier.combat.util.Path + * + * @author 坏黑 + * @since 2023/5/28 21:30 + */ +class Path(val start: Vector, val end: Vector, center: Vector) { + + /** 方向 */ + val direction = Direction.get(start, end, center) + + /** 开始坐标 */ + fun getStartLocation(world: World): Location { + return start.toLocation(world) + } + + /** 结束坐标 */ + fun getEndLocation(world: World): Location { + return end.toLocation(world) + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/blade/Rotator.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/blade/Rotator.kt new file mode 100644 index 0000000..5d7fb61 --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/blade/Rotator.kt @@ -0,0 +1,47 @@ +package me.asgard.frontier.jarnbjorn.api.blade + +import org.bukkit.Location +import org.bukkit.util.Vector + +/** + * FrontierCombat + * me.asgard.frontier.combat.util.Rotator + * + * @author 坏黑 + * @since 2023/5/27 18:56 + */ +class Rotator(val base: Vector, val yaw: Float, val pitch: Float, val originPath: Path) { + + /** 本地X轴(相对玩家自身的X轴) */ + val axisX = Vector(1, 0, 0) + + init { + // 设置玩家自身参考系的X轴朝向(yaw为-180度时朝向[1,0,0] 因此将实际的yaw减去-180度就是应该旋转的角度 + // 记得是逆时针旋转 所以用-180度减去实际的yaw) + axisX.rotateAroundY((-180 - yaw) * Math.PI / 180) + } + + /** + * 将标记点绕玩家旋转 + */ + fun rotate(newLocation: Location): Path { + // 玩家视角转动角度(单位为弧度) + val deltaYaw = (newLocation.yaw - yaw) * Math.PI / 180 + val deltaPitch = (newLocation.pitch - pitch) * Math.PI / 180 + // 将标记点调整到(玩家移动前)的坐标系 + val newMarker1Vec = originPath.start.clone().subtract(base) + val newMarker2Vec = originPath.end.clone().subtract(base) + // 在这个坐标系绕Y轴(即玩家所在位置竖直的一条轴)旋转(逆时针旋转所以要取反) + newMarker1Vec.rotateAroundY(-deltaYaw) + newMarker2Vec.rotateAroundY(-deltaYaw) + // 此坐标系(即玩家)自身的X轴需跟着旋转 + val newAxisX = axisX.clone().rotateAroundY(-deltaYaw) + // 接着将标记点绕【旋转后的X轴】旋转 + newMarker1Vec.rotateAroundAxis(newAxisX.normalize(), -deltaPitch) + newMarker2Vec.rotateAroundAxis(newAxisX.normalize(), -deltaPitch) + // 将标记点传送到(玩家移动后)的的坐标系 + val r1 = newLocation.clone().add(newMarker1Vec).toVector() + val r2 = newLocation.clone().add(newMarker2Vec).toVector() + return Path(r1, r2, newLocation.toVector()) + } +} diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/event/PluginReloadEvent.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/event/PluginReloadEvent.kt new file mode 100644 index 0000000..7cfe28b --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/event/PluginReloadEvent.kt @@ -0,0 +1,24 @@ +package me.asgard.frontier.jarnbjorn.api.event + +import taboolib.platform.type.BukkitProxyEvent + +/** + * 重载事件 + * + * @author 坏黑 + * @since 2024/2/14 10:15 + */ +class PluginReloadEvent { + + class Pre : BukkitProxyEvent() { + + override val allowCancelled: Boolean + get() = false + } + + class Post : BukkitProxyEvent() { + + override val allowCancelled: Boolean + get() = false + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/template/BladeTemplate.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/template/BladeTemplate.kt new file mode 100644 index 0000000..ededcb6 --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/template/BladeTemplate.kt @@ -0,0 +1,48 @@ +package me.asgard.frontier.jarnbjorn.api.template + +import me.asgard.frontier.jarnbjorn.api.blade.BladePainter +import org.bukkit.Location +import taboolib.common.io.newFile +import taboolib.common.platform.function.getDataFolder +import taboolib.library.configuration.ConfigurationSection + +/** + * Jarnbjorn + * me.asgard.frontier.jarnbjorn.api.template.BladeTemplate + * + * @author 坏黑 + * @since 2024/2/29 21:34 + */ +abstract class BladeTemplate(val id: String, val root: ConfigurationSection) { + + /** 坐标 */ + abstract val location: Location + + /** 起始点 */ + abstract var startPoint: Location + + /** 结束点 */ + abstract var endPoint: Location + + /** 生成演示实体 */ + abstract fun spawnDemoEntity() + + /** 移除演示实体 */ + abstract fun removeDemoEntity() + + /** 在演示实体上播放刀刃效果 */ + abstract fun spawnDemoEffect() + + /** 移动模板位置 */ + abstract fun moveTo(location: Location) + + /** 绘制 */ + open fun painter(): BladePainter { + return BladePainter(this) + } + + /** 保存配置 */ + open fun save() { + newFile(getDataFolder(), "template/$id.yml").writeText(root.toString()) + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/template/BladeTemplateManager.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/template/BladeTemplateManager.kt new file mode 100644 index 0000000..e20390c --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/api/template/BladeTemplateManager.kt @@ -0,0 +1,38 @@ +package me.asgard.frontier.jarnbjorn.api.template + +import org.bukkit.Location + +/** + * Jarnbjorn + * me.asgard.frontier.jarnbjorn.api.template.BladeTemplateManager + * + * @author 坏黑 + * @since 2024/2/29 21:35 + */ +interface BladeTemplateManager { + + /** + * 获取模板 + */ + fun get(id: String): BladeTemplate? + + /** + * 获取所有 + */ + fun getAll(): List + + /** + * 创建模板 + */ + fun create(id: String, location: Location): BladeTemplate + + /** + * 删除模板 + */ + fun remove(id: String): Boolean + + /** + * 重载模板 + */ + fun reload() +} \ No newline at end of file diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/DefaultImpl.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/DefaultImpl.kt new file mode 100644 index 0000000..997cca1 --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/DefaultImpl.kt @@ -0,0 +1,23 @@ +package me.asgard.frontier.jarnbjorn.impl + +import me.asgard.frontier.jarnbjorn.Jarnbjorn +import me.asgard.frontier.jarnbjorn.impl.template.DefaultTemplateManager +import taboolib.common.LifeCycle +import taboolib.common.platform.Awake + +/** + * Jarnbjorn + * me.asgard.frontier.jarnbjorn.impl.DefaultImpl + * + * @author 坏黑 + * @since 2024/3/3 14:39 + */ +object DefaultImpl { + + @Awake(LifeCycle.LOAD) + fun init() { + if (!Jarnbjorn.isBladeTemplateManagerInitialized()) { + Jarnbjorn.bladeTemplateManager = DefaultTemplateManager() + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/command/CommandMain.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/command/CommandMain.kt new file mode 100644 index 0000000..c1075f2 --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/command/CommandMain.kt @@ -0,0 +1,39 @@ +package me.asgard.frontier.jarnbjorn.impl.command + +import me.asgard.frontier.jarnbjorn.Jarnbjorn +import org.bukkit.command.CommandSender +import taboolib.common.platform.command.CommandBody +import taboolib.common.platform.command.CommandHeader +import taboolib.common.platform.command.mainCommand +import taboolib.common.platform.command.subCommand +import taboolib.expansion.createHelper + +/** + * Jarnbjorn + * me.asgard.frontier.jarnbjorn.impl.command.CommandMain + * + * @author 坏黑 + * @since 2024/2/29 21:30 + */ +@CommandHeader("jarnbjorn", ["jb"]) +object CommandMain { + + val prefix: String + get() = "§7[ §f§nJarnbjorn§7 ]§8" + + @CommandBody + val main = mainCommand { + createHelper() + } + + @CommandBody(aliases = ["t"]) + val template = CommandTemplate + + @CommandBody + val reload = subCommand { + execute { sender, _, _ -> + Jarnbjorn.reload() + sender.sendMessage("$prefix Reloaded.") + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/command/CommandTemplate.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/command/CommandTemplate.kt new file mode 100644 index 0000000..bce9f8d --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/command/CommandTemplate.kt @@ -0,0 +1,86 @@ +package me.asgard.frontier.jarnbjorn.impl.command + +import me.asgard.frontier.jarnbjorn.Jarnbjorn +import org.bukkit.entity.Player +import taboolib.common.platform.command.CommandBody +import taboolib.common.platform.command.subCommand +import taboolib.common.platform.command.suggest + +/** + * Jarnbjorn + * me.asgard.frontier.jarnbjorn.impl.command.CommandTemplate + * + * @author 坏黑 + * @since 2024/2/29 21:33 + */ +object CommandTemplate { + + val prefix: String + get() = "§7[ §f§nJarnbjorn§7 ]§8" + + /** + * 创建模板 + */ + @CommandBody + val create = subCommand { + dynamic("id") { + execute { sender, ctx, _ -> + val id = ctx["id"] + if (Jarnbjorn.bladeTemplateManager.get(id) != null) { + sender.sendMessage("$prefix 模板已存在。") + return@execute + } + Jarnbjorn.bladeTemplateManager.create(ctx["id"], sender.location) + sender.sendMessage("$prefix 模板已创建。") + } + } + } + + /** + * 移除模板 + */ + @CommandBody + val remove = subCommand { + dynamic("id") { + suggest { Jarnbjorn.bladeTemplateManager.getAll().map { it.id } } + execute { sender, ctx, _ -> + val id = ctx["id"] + if (Jarnbjorn.bladeTemplateManager.remove(id)) { + sender.sendMessage("$prefix 模板已移除。") + } else { + sender.sendMessage("$prefix 模板不存在。") + } + } + } + } + + /** + * 移动模版位置 + */ + @CommandBody + val move = subCommand { + dynamic("id") { + suggest { Jarnbjorn.bladeTemplateManager.getAll().map { it.id } } + execute { sender, ctx, _ -> + val template = Jarnbjorn.bladeTemplateManager.get(ctx["id"])!! + template.moveTo(sender.location) + sender.sendMessage("$prefix 模板已移动。") + } + } + } + + /** + * 传送到模版位置 + */ + @CommandBody + val tp = subCommand { + dynamic("id") { + suggest { Jarnbjorn.bladeTemplateManager.getAll().map { it.id } } + execute { sender, ctx, _ -> + val template = Jarnbjorn.bladeTemplateManager.get(ctx["id"])!! + sender.teleport(template.location) + sender.sendMessage("$prefix 已传送到模板位置。") + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/template/DefaultTemplate.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/template/DefaultTemplate.kt new file mode 100644 index 0000000..1cf4ed6 --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/template/DefaultTemplate.kt @@ -0,0 +1,115 @@ +package me.asgard.frontier.jarnbjorn.impl.template + +import ink.ptms.adyeshach.core.Adyeshach +import ink.ptms.adyeshach.core.entity.EntityTypes +import ink.ptms.adyeshach.core.entity.manager.ManagerType +import ink.ptms.adyeshach.core.entity.type.AdyHuman +import ink.ptms.adyeshach.core.util.modify +import ink.ptms.adyeshach.core.util.plus +import me.asgard.frontier.jarnbjorn.api.template.BladeTemplate +import org.bukkit.Location +import org.bukkit.Material +import org.bukkit.Particle +import org.bukkit.inventory.EquipmentSlot +import org.bukkit.inventory.ItemStack +import taboolib.library.configuration.ConfigurationSection +import taboolib.module.configuration.Configuration +import taboolib.module.configuration.Type +import taboolib.module.configuration.util.getLocation +import taboolib.module.configuration.util.setLocation +import taboolib.module.nms.createPacket +import taboolib.module.nms.sendBundlePacket +import taboolib.platform.util.onlinePlayers +import taboolib.platform.util.toBukkitLocation +import taboolib.platform.util.toProxyLocation + +/** + * Jarnbjorn + * me.asgard.frontier.jarnbjorn.impl.template.DefaultTemplate + * + * @author 坏黑 + * @since 2024/2/29 22:17 + */ +class DefaultTemplate(id: String, root: ConfigurationSection) : BladeTemplate(id, root) { + + override var location = root.getLocation("location")?.toBukkitLocation() ?: Location(null, 0.0, 0.0, 0.0) + get() = field.clone() + set(value) { + field = value + root.setLocation("location", value.toProxyLocation()) + save() + } + + override var startPoint = root.getLocation("startPoint")?.toBukkitLocation() ?: Location(null, 0.0, 0.0, 0.0) + get() = field.clone() + set(value) { + field = value + root.setLocation("startPoint", value.toProxyLocation()) + save() + } + + override var endPoint = root.getLocation("endPoint")?.toBukkitLocation() ?: Location(null, 0.0, 0.0, 0.0) + get() = field.clone() + set(value) { + field = value + root.setLocation("endPoint", value.toProxyLocation()) + save() + } + + var demoEntity: AdyHuman? = null + + constructor(id: String, location: Location) : this(id, Configuration.empty(Type.YAML, concurrent = false)) { + // 获取标准化坐标, 使 Yaw 和 Pitch 恢复初始状态 + this.location = location.blockLocation().modify(yaw = 0f, pitch = 0f) + // 设置默认起始点和结束点 + val eyeLocation = this.location.add(0.5, 2.0, 0.5) + this.startPoint = eyeLocation.clone().also { + it.yaw = 45f + it.pitch = -45f + it.add(it.direction.multiply(2.0)) + } + this.endPoint = eyeLocation.clone().also { + it.yaw = -45f + it.pitch = 45f + it.add(it.direction.multiply(2.0)) + } + } + + override fun spawnDemoEntity() { + if (location.world == null) return + demoEntity = Adyeshach.api().getPublicEntityManager(ManagerType.TEMPORARY).create(EntityTypes.PLAYER, location.fix()) { + it as AdyHuman + it.isNitwit = true + it.setTag("jb_template", this) + it.setDerived("jb_template") + it.setName(id) + it.setEquipment(EquipmentSlot.HAND, ItemStack(Material.IRON_SWORD)) + } as AdyHuman + } + + override fun removeDemoEntity() { + demoEntity?.remove() + } + + override fun spawnDemoEffect() { + val packets = arrayListOf( + Particle.SOUL_FIRE_FLAME.createPacket(startPoint, count = 10), + Particle.SOUL_FIRE_FLAME.createPacket(startPoint.toVector().midpoint(endPoint.toVector()).toLocation(location.world!!), count = 10), + Particle.SOUL_FIRE_FLAME.createPacket(endPoint, count = 10) + ) + onlinePlayers.filter { it.world == location.world && it.location.distance(location) < 32 }.forEach { it.sendBundlePacket(packets) } + } + + override fun moveTo(location: Location) { +// this.location = location.blockLocation().modify(yaw = 0f, pitch = 0f) +// this.demoEntity?.teleport(this.location.fix()) + } + + fun Location.blockLocation(): Location { + return block.location + } + + fun Location.fix(): Location { + return clone().add(0.5, 0.0, 0.5) + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/template/DefaultTemplateManager.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/template/DefaultTemplateManager.kt new file mode 100644 index 0000000..28c6b4a --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/template/DefaultTemplateManager.kt @@ -0,0 +1,65 @@ +package me.asgard.frontier.jarnbjorn.impl.template + +import me.asgard.frontier.jarnbjorn.api.template.BladeTemplate +import me.asgard.frontier.jarnbjorn.api.template.BladeTemplateManager +import org.bukkit.Location +import taboolib.common.io.newFile +import taboolib.common.platform.function.getDataFolder +import taboolib.common.platform.function.info +import taboolib.common.platform.function.submitAsync +import taboolib.module.configuration.Configuration +import java.io.File + +/** + * Jarnbjorn + * me.asgard.frontier.jarnbjorn.impl.template.DefaultTemplateManager + * + * @author 坏黑 + * @since 2024/2/29 22:17 + */ +class DefaultTemplateManager : BladeTemplateManager { + + val templateList = HashMap() + + init { + submitAsync(period = 20) { templateList.values.forEach { it.spawnDemoEffect() } } + } + + override fun get(id: String): BladeTemplate? { + return templateList[id] + } + + override fun getAll(): List { + return templateList.values.toList() + } + + override fun create(id: String, location: Location): BladeTemplate { + return DefaultTemplate(id, location).also { + it.spawnDemoEntity() + templateList[id] = it + } + } + + override fun remove(id: String): Boolean { + val template = templateList.remove(id) ?: return false + val file = File(getDataFolder(), "template/$id.yml") + if (file.exists()) { + file.renameTo(newFile(getDataFolder(), "template/trash/$id.yml.bak")) + file.delete() + } + template.removeDemoEntity() + templateList.remove(id) + return true + } + + override fun reload() { + templateList.values.forEach { it.removeDemoEntity() } + templateList.clear() + File(getDataFolder(), "template").walk().filter { it.extension == "yml" }.forEach { + val template = DefaultTemplate(it.nameWithoutExtension, Configuration.loadFromFile(it)) + template.spawnDemoEntity() + templateList[template.id] = template + } + info("Loaded ${templateList.size} blade template(s).") + } +} \ No newline at end of file diff --git a/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/template/TemplateEditor.kt b/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/template/TemplateEditor.kt new file mode 100644 index 0000000..213ac02 --- /dev/null +++ b/src/main/kotlin/me/asgard/frontier/jarnbjorn/impl/template/TemplateEditor.kt @@ -0,0 +1,257 @@ +package me.asgard.frontier.jarnbjorn.impl.template + +import ink.ptms.adyeshach.core.bukkit.BukkitAnimation +import ink.ptms.adyeshach.core.event.AdyeshachEntityInteractEvent +import ink.ptms.adyeshach.core.util.plus +import me.asgard.frontier.jarnbjorn.Jarnbjorn +import org.bukkit.Color +import org.bukkit.Location +import org.bukkit.Material +import org.bukkit.Particle +import org.bukkit.entity.ArmorStand +import org.bukkit.entity.Player +import org.bukkit.event.block.BlockBreakEvent +import org.bukkit.event.player.* +import taboolib.common.platform.event.SubscribeEvent +import taboolib.module.nms.PacketReceiveEvent +import taboolib.module.nms.createPacket +import taboolib.platform.util.* + +/** + * 编辑器 + * + * 左键 | 右键: 切换控制点 + * 滚轮: 切换观察角度 + * W, A, S, D: 移动控制点(前后左右) + * SPACE:移动控制点(上) + * SHIFT:移动控制点(下) + * F: 播放动画 + * Q: 离开 + */ +class TemplateEditor(val player: Player, val template: DefaultTemplate) { + + var locationIdx = 0 + val cameraPos = arrayOf("斜上方", "正前方", "右侧", "正后方", "左侧") + + /** 载具位置 */ + val vehicle = player.world.spawn(getLocation(), ArmorStand::class.java) { + it.isVisible = false + it.setAI(false) + it.setGravity(false) + } + + init { + player.inventory.heldItemSlot = 0 + player.inventory.setItem(0, buildItem(Material.SPECTRAL_ARROW) { + name = "&e鸡八" + lore += "&6编辑魔杖" + colored() + }) + look() + vehicle.addPassenger(player) + } + + fun destroy() { + vehicle.remove() + player.inventory.takeItem(999) { it.type == Material.SPECTRAL_ARROW } + player.removeMeta("jr_edit") + } + + fun getLocation(): Location { + val distance = 4.0 + return when (locationIdx) { + // 斜上方 + 0 -> template.location.add(distance, 1.0, distance) + // 正前方 + 1 -> template.location.add(0.5, 0.0, distance) + // 右侧 + 2 -> template.location.add(-distance, 0.0, 0.5) + // 正后方 + 3 -> template.location.add(0.5, 1.0, -distance) + // 左侧 + 4 -> template.location.add(distance, 0.0, 0.5) + // 其他(正前方) + else -> template.location.add(0.5, 0.0, distance) + } + } + + fun look() { + val demoEntity = template.demoEntity ?: return + val loc = player.location.clone() + loc.direction = demoEntity.getLocation().subtract(vehicle.location.add(0.0, 2.0, 0.0)).toVector().normalize() + player.setRotation(loc.yaw, loc.pitch) + } + + fun nextLocation() { + locationIdx += 1 + locationIdx %= 5 + updateLocation() + } + + fun previousLocation() { + locationIdx -= 1 + if (locationIdx < 0) { + locationIdx = 4 + } + updateLocation() + } + + fun updateLocation() { + vehicle.removePassenger(player) + vehicle.teleport(getLocation()) + look() + vehicle.addPassenger(player) + val pos = arrayListOf() + cameraPos.forEachIndexed { index, s -> + pos += (if (index == locationIdx) "§e$s" else "§7$s") + } + player.sendTitle("§f", pos.joinToString("§r/"), 5, 40, 5) + } + + fun show() { + template.demoEntity?.sendAnimation(BukkitAnimation.SWING_MAIN_HAND) + template.painter().step(0.002).draw().groupSize(5).build { i, pos -> + Particle.REDSTONE.createPacket(pos, count = 3, data = Particle.DustOptions(Color.WHITE, 1f)) + }.sendTo(player, 2, 5) + } + + companion object { + + @SubscribeEvent + private fun onJoin(e: PlayerJoinEvent) { + e.player.removeMeta("jr_edit") + } + + @SubscribeEvent + private fun onQuit(e: PlayerQuitEvent) { + e.player.getEditor()?.destroy() + } + + @SubscribeEvent + private fun onBreak(e: BlockBreakEvent) { + if (e.player.isEditMode()) { + e.isCancelled = true + } + } + + @SubscribeEvent + private fun onClick(e: AdyeshachEntityInteractEvent) { + if (e.isMainHand) { + val template = e.entity.getTag("jb_template") as? DefaultTemplate ?: return + if (!e.player.isEditMode()) { + e.player.setMeta("jr_edit", TemplateEditor(e.player, template)) + e.player.sendTitle("§r", "§e进入 Jarnbjorn 编辑模式", 5, 40, 5) + } + } + } + + /** + * 左键 + */ + @SubscribeEvent + private fun onAnimation(e: PlayerAnimationEvent) { + if (e.animationType == PlayerAnimationType.ARM_SWING) { + if (e.player.isEditMode()) { + e.player.sendMessage("left") + } else { + val template = Jarnbjorn.bladeTemplateManager.get("test") ?: return + template.painter().use(e.player.eyeLocation).step(0.002).groupSize(5).draw().build { _, pos -> + Particle.REDSTONE.createPacket(pos, count = 5, data = Particle.DustOptions(Color.WHITE, 1f)) + }.sendTo(e.player, 2, 5) + } + } + } + + /** + * 右键 + */ + @SubscribeEvent + private fun onInteract(e: PlayerInteractEvent) { + if (e.player.isEditMode() && e.isRightClick()) { + e.player.sendMessage("right") + } + } + + /** + * 右键 + */ + @SubscribeEvent + private fun onInteract(e: AdyeshachEntityInteractEvent) { + if (e.player.isEditMode() && e.isMainHand) { + e.isCancelled = true + e.player.sendMessage("right") + } + } + + /** + * 丢弃 + */ + @SubscribeEvent + private fun onDrop(e: PlayerDropItemEvent) { + if (e.player.isEditMode()) { + e.isCancelled = true + e.player.getEditor()?.destroy() + } + } + + /** + * F + */ + @SubscribeEvent + private fun onSwap(e: PlayerSwapHandItemsEvent) { + if (e.player.isEditMode()) { + e.isCancelled = true + e.player.getEditor()?.show() + } + } + + /** + * 滚轮 + */ + @SubscribeEvent + private fun onHeld(e: PlayerItemHeldEvent) { + if (e.player.isEditMode()) { + e.isCancelled = true + if (e.newSlot == 1) { + e.player.sendMessage("next") + e.player.getEditor()?.nextLocation() + } + if (e.newSlot == 8) { + e.player.sendMessage("previous") + e.player.getEditor()?.previousLocation() + } + } + } + + /** + * 移动 + */ + @SubscribeEvent + private fun onPacket(e: PacketReceiveEvent) { + if (e.packet.name == "PacketPlayInSteerVehicle" && e.player.isEditMode()) { + e.isCancelled = true + val xxa = e.packet.read("xxa")!! + val zza = e.packet.read("zza")!! + val jump = e.packet.read("isJumping")!! + val shift = e.packet.read("isShiftKeyDown")!! + if (jump) { + e.player.sendMessage("jump") + } + if (shift) { + e.player.sendMessage("shift") + } + if (xxa != 0f || zza != 0f) { + e.player.sendMessage("move $xxa $zza") + } + } + } + } +} + +internal fun Player.isEditMode(): Boolean { + return hasMetadata("jr_edit") +} + +internal fun Player.getEditor(): TemplateEditor? { + return getMetaFirstOrNull("jr_edit")?.castOrNull() +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..f59260b --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,9 @@ +# 绕过反作弊 +# 特殊攻击时赋予临时权限 +prevent-anti-cheat: + enable: true + permissions: + - nocheatplus, + - nocheatplus.checks, + - anticheat.check.exempt, + - aac.bypass \ No newline at end of file