diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index ddc82a66..78e31774 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -32,6 +32,7 @@
+
diff --git a/BANNER.md b/BANNER.md
index 334b3dd2..0c304829 100644
--- a/BANNER.md
+++ b/BANNER.md
@@ -61,5 +61,5 @@ This plugin utilizes a boss bar, but you can still use the first boss bar.
No
### Version
-Plugin: 1.18.2-1.21.3
-Fabric mod: server 1.21.3
\ No newline at end of file
+Plugin: 1.18.2-1.21.4
+Fabric mod: server 1.21.4
\ No newline at end of file
diff --git a/README.md b/README.md
index ef10ad1c..462fbe4b 100644
--- a/README.md
+++ b/README.md
@@ -23,9 +23,9 @@ This project implements a server-side HUD.
- Supports animation.
### Platform
-- Bukkit(including Folia) 1.18.2-1.21.3
+- Bukkit(including Folia) 1.18.2-1.21.4
- Velocity 3.3-3.4
-- Fabric server 1.21.3
+- Fabric server 1.21.4
### Library
- [kotlin stdlib](https://github.com/JetBrains/kotlin): Implements better functional programming.
@@ -54,6 +54,21 @@ Requires Java 17, 21 Eclipse Adoptium.
- Build dokka-based docs jar: ./gradlew dokkaJar
### API
+Get from maven central
+``` kotlin
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ compileOnly("net.kyori:adventure-api:VERSION") //Adventure api
+ compileOnly("io.github.toxicity188:BetterHud-standard-api:VERSION") //Standard api
+ compileOnly("io.github.toxicity188:BetterHud-bukkit-api:VERSION") //Platform api
+ compileOnly("io.github.toxicity188:BetterCommand:VERSION") //BetterCommand library
+}
+```
+
+Get from Jitpack
[![](https://jitpack.io/v/toxicity188/BetterHud.svg)](https://jitpack.io/#toxicity188/BetterHud)
``` kotlin
repositories {
@@ -62,9 +77,9 @@ repositories {
}
dependencies {
- compileOnly("net.kyori:adventure-api:VERSION") //Adventure api.
- compileOnly("com.github.toxicity188:BetterHud:VERSION") //BetterHud.
- compileOnly("com.github.toxicity188:BetterCommand:VERSION") //BetterCommand library.
+ compileOnly("net.kyori:adventure-api:VERSION") //Adventure api
+ compileOnly("com.github.toxicity188:BetterHud:VERSION") //BetterHud
+ compileOnly("com.github.toxicity188:BetterCommand:VERSION") //BetterCommand library
}
```
diff --git a/bootstrap/bukkit/build.gradle.kts b/bootstrap/bukkit/build.gradle.kts
index 526cc5d1..70137628 100644
--- a/bootstrap/bukkit/build.gradle.kts
+++ b/bootstrap/bukkit/build.gradle.kts
@@ -15,8 +15,14 @@ dependencies {
compileOnly("net.Indyuce:MMOCore-API:1.13.1-SNAPSHOT")
compileOnly("net.Indyuce:MMOItems-API:6.10-SNAPSHOT")
compileOnly("me.clip:placeholderapi:2.11.6")
- compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.4.0-SNAPSHOT")
- compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.1.0-SNAPSHOT")
+ compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.4.0-SNAPSHOT") {
+ exclude("com.google.guava")
+ exclude("com.google.code.gson")
+ }
+ compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.1.0-SNAPSHOT") {
+ exclude("com.google.guava")
+ exclude("com.google.code.gson")
+ }
compileOnly("com.github.MilkBowl:VaultAPI:1.7.1")
compileOnly("com.github.SkriptLang:Skript:2.9.5")
compileOnly("net.skinsrestorer:skinsrestorer-api:15.4.4")
diff --git a/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/BukkitBootstrapImpl.kt b/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/BukkitBootstrapImpl.kt
index f9b85465..fbb18f16 100644
--- a/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/BukkitBootstrapImpl.kt
+++ b/bootstrap/bukkit/src/main/kotlin/kr/toxicity/hud/bootstrap/bukkit/BukkitBootstrapImpl.kt
@@ -170,6 +170,7 @@ class BukkitBootstrapImpl : BukkitBootstrap, JavaPlugin() {
override fun onLoad() {
val pluginManager = Bukkit.getPluginManager()
nms = when (MinecraftVersion.current) {
+ MinecraftVersion.version1_21_4 -> kr.toxicity.hud.nms.v1_21_R3.NMSImpl()
MinecraftVersion.version1_21_2, MinecraftVersion.version1_21_3 -> kr.toxicity.hud.nms.v1_21_R2.NMSImpl()
MinecraftVersion.version1_21, MinecraftVersion.version1_21_1 -> kr.toxicity.hud.nms.v1_21_R1.NMSImpl()
MinecraftVersion.version1_20_5, MinecraftVersion.version1_20_6 -> kr.toxicity.hud.nms.v1_20_R4.NMSImpl()
diff --git a/bootstrap/fabric/src/main/kotlin/kr/toxicity/hud/bootstrap/fabric/FabricBootstrapImpl.kt b/bootstrap/fabric/src/main/kotlin/kr/toxicity/hud/bootstrap/fabric/FabricBootstrapImpl.kt
index 10e964b5..a83d24f5 100644
--- a/bootstrap/fabric/src/main/kotlin/kr/toxicity/hud/bootstrap/fabric/FabricBootstrapImpl.kt
+++ b/bootstrap/fabric/src/main/kotlin/kr/toxicity/hud/bootstrap/fabric/FabricBootstrapImpl.kt
@@ -242,9 +242,9 @@ class FabricBootstrapImpl : FabricBootstrap, DedicatedServerModInitializer {
}
- override fun minecraftVersion(): String = "1.21.3"
+ override fun minecraftVersion(): String = "1.21.4"
- override fun mcmetaVersion(): Int = 42
+ override fun mcmetaVersion(): Int = 46
private val uuidMap = ConcurrentHashMap()
diff --git a/bootstrap/velocity/src/main/kotlin/kr/toxicity/hud/bootstrap/velocity/VelocityBootstrapImpl.kt b/bootstrap/velocity/src/main/kotlin/kr/toxicity/hud/bootstrap/velocity/VelocityBootstrapImpl.kt
index 582452f3..8eae5b3d 100644
--- a/bootstrap/velocity/src/main/kotlin/kr/toxicity/hud/bootstrap/velocity/VelocityBootstrapImpl.kt
+++ b/bootstrap/velocity/src/main/kotlin/kr/toxicity/hud/bootstrap/velocity/VelocityBootstrapImpl.kt
@@ -233,8 +233,8 @@ class VelocityBootstrapImpl @Inject constructor(
}
}
- override fun minecraftVersion(): String = "1.21.3"
- override fun mcmetaVersion(): Int = 42
+ override fun minecraftVersion(): String = "1.21.4"
+ override fun mcmetaVersion(): Int = 46
override fun useLegacyFont(): Boolean = false
override fun world(name: String): WorldWrapper? = null
diff --git a/build.gradle.kts b/build.gradle.kts
index 3e21ffa6..a74afb9b 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -59,7 +59,8 @@ val supportedMinecraftVersions = listOf(
"1.21",
"1.21.1",
"1.21.2",
- "1.21.3"
+ "1.21.3",
+ "1.21.4"
)
val supportedVelocityVersions = listOf(
"3.3",
@@ -153,6 +154,7 @@ val currentNmsVersion = listOf(
"v1_20_R4",
"v1_21_R1",
"v1_21_R2",
+ "v1_21_R3",
).map {
project("nms:$it")
}
diff --git a/changelog/1.10.md b/changelog/1.10.md
index 3b351d4c..85f316d9 100644
--- a/changelog/1.10.md
+++ b/changelog/1.10.md
@@ -5,6 +5,7 @@
- Now support about Nexo is available.
## Add
+- 1.21.4 support
- Add 'children' in image.
```yaml
children_full:
@@ -117,6 +118,8 @@ test_text:
```yaml
disable-legacy-offset: false #If this is true, a correct pixel offset is provided.
```
+- Add 'hud turn on|off ()
+
+ @Suppress("UNCHECKED_CAST")
+ private val operation = ClientboundBossEventPacket::class.java.declaredClasses.first {
+ it.isEnum
+ } as Class>
+
+ private val operationEnum = operation.enumConstants
+ private val getConnection: (ServerCommonPacketListenerImpl) -> Connection = if (BetterHudAPI.inst().bootstrap().isPaper) {
+ {
+ it.connection
+ }
+ } else {
+ ServerCommonPacketListenerImpl::class.java.declaredFields.first { f ->
+ f.type == Connection::class.java
+ }.apply {
+ isAccessible = true
+ }.let { get ->
+ {
+ get[it] as Connection
+ }
+ }
+ }
+
+ fun createBossBar(byteBuf: RegistryFriendlyByteBuf): ClientboundBossEventPacket = ClientboundBossEventPacket.STREAM_CODEC.decode(byteBuf)
+
+ private fun toAdventure(component: net.minecraft.network.chat.Component) = GsonComponentSerializer.gson().deserialize(CraftChatMessage.toJSON(component))
+ private fun fromAdventure(component: Component) = CraftChatMessage.fromJSON(GsonComponentSerializer.gson().serialize(component))
+ private fun getColor(color: BossBar.Color) = when (color) {
+ BossBar.Color.PINK -> BossEvent.BossBarColor.PINK
+ BossBar.Color.BLUE -> BossEvent.BossBarColor.BLUE
+ BossBar.Color.RED -> BossEvent.BossBarColor.RED
+ BossBar.Color.GREEN -> BossEvent.BossBarColor.GREEN
+ BossBar.Color.YELLOW -> BossEvent.BossBarColor.YELLOW
+ BossBar.Color.PURPLE -> BossEvent.BossBarColor.PURPLE
+ BossBar.Color.WHITE -> BossEvent.BossBarColor.WHITE
+ }
+ }
+
+ override fun inject(player: HudPlayer, color: BossBar.Color) {
+ val h = player.handle() as CraftPlayer
+ bossBarMap.computeIfAbsent(h.uniqueId) {
+ PlayerBossBar(h, h.handle.connection, color, Component.empty())
+ }
+ }
+ override fun showBossBar(player: HudPlayer, color: BossBar.Color, component: Component) {
+ bossBarMap[player.uuid()]?.update(color, component)
+ }
+
+ override fun removeBossBar(player: HudPlayer) {
+ bossBarMap.remove(player.uuid())?.remove()
+ }
+
+ override fun reloadBossBar(player: HudPlayer, color: BossBar.Color) {
+ bossBarMap[player.uuid()]?.resetDummy(color)
+ }
+
+ override fun getVersion(): NMSVersion {
+ return NMSVersion.V1_21_R3
+ }
+
+ override fun getTextureValue(player: HudPlayer): String {
+ return (player.handle() as CraftPlayer).handle.gameProfile.properties.get("textures").first().value
+ }
+
+
+ override fun registerCommand(module: CommandModule) {
+ val dispatcher = (Bukkit.getServer() as CraftServer).server.commands.dispatcher
+ module.build { s: CommandSourceStack ->
+ when (val sender = s.bukkitSender) {
+ is ConsoleCommandSender -> BetterHudAPI.inst().bootstrap().consoleSource()
+ is Player -> BetterHudAPI.inst().playerManager.getHudPlayer(sender.uniqueId)
+ else -> null
+ }
+ }.forEach {
+ dispatcher.register(it)
+ }
+ }
+
+ override fun getFoliaAdaptedPlayer(player: Player): Player {
+ val handle = (player as CraftPlayer).handle
+ return object : CraftPlayer(Bukkit.getServer() as CraftServer, handle) {
+ override fun getPersistentDataContainer(): CraftPersistentDataContainer {
+ return player.persistentDataContainer
+ }
+ override fun getHandle(): ServerPlayer {
+ return handle
+ }
+ override fun getHealth(): Double {
+ return player.health
+ }
+ override fun getScaledHealth(): Float {
+ return player.scaledHealth
+ }
+ override fun getHealthScale(): Double {
+ return player.healthScale
+ }
+ override fun getFirstPlayed(): Long {
+ return player.firstPlayed
+ }
+ override fun getInventory(): PlayerInventory {
+ return player.inventory
+ }
+ override fun getEnderChest(): Inventory {
+ return player.enderChest
+ }
+ override fun isOp(): Boolean {
+ return player.isOp
+ }
+ override fun getGameMode(): GameMode {
+ return player.gameMode
+ }
+ override fun getEquipment(): EntityEquipment {
+ return player.equipment
+ }
+ override fun hasPermission(name: String): Boolean {
+ return player.hasPermission(name)
+ }
+ override fun hasPermission(perm: Permission): Boolean {
+ return player.hasPermission(perm)
+ }
+ override fun isPermissionSet(name: String): Boolean {
+ return player.isPermissionSet(name)
+ }
+ override fun isPermissionSet(perm: Permission): Boolean {
+ return player.isPermissionSet(perm)
+ }
+ override fun hasPlayedBefore(): Boolean {
+ return player.hasPlayedBefore()
+ }
+ override fun getWorldBorder(): WorldBorder? {
+ return player.getWorldBorder()
+ }
+ override fun showBossBar(bar: BossBar) {
+ player.showBossBar(bar)
+ }
+ override fun hideBossBar(bar: BossBar) {
+ player.hideBossBar(bar)
+ }
+ override fun sendMessage(message: String) {
+ player.sendMessage(message)
+ }
+ override fun getLastDamageCause(): EntityDamageEvent? {
+ return player.lastDamageCause
+ }
+ override fun pointers(): Pointers {
+ return player.pointers()
+ }
+ override fun spigot(): Player.Spigot {
+ return player.spigot()
+ }
+ }
+ }
+
+
+ private class CachedHudBossbar(val hud: HudBossBar, val cacheUUID: UUID, val buf: HudByteBuf)
+ private class PlayerBossBar(val player: Player, val listener: ServerGamePacketListenerImpl, color: BossBar.Color, component: Component): ChannelDuplexHandler() {
+ private inner class PlayerDummyBossBar(color: BossBar.Color) {
+ val line = BetterHudAPI.inst().configManager.bossbarLine - 1
+ val dummyBars = (0..())
+ private val otherBarCache = ConcurrentLinkedQueue>()
+ private val uuid = UUID.randomUUID().apply {
+ listener.send(ClientboundBossEventPacket.createAddPacket(HudBossBar(this, component, color)))
+ }
+
+ private var last: HudBossBar = HudBossBar(uuid, Component.empty(), color)
+ private var onUse = uuid to HudByteBuf(Unpooled.buffer())
+
+ init {
+ val pipeLine = getConnection(listener).channel.pipeline()
+ pipeLine.toMap().forEach {
+ if (it.value is Connection) pipeLine.addBefore(it.key, INJECT_NAME, this)
+ }
+ }
+
+ fun update(color: BossBar.Color, component: Component) {
+ val bossBar = HudBossBar(uuid, component, color)
+ last = bossBar
+ listener.send(ClientboundBossEventPacket.createUpdateNamePacket(bossBar))
+ }
+
+ fun resetDummy(color: BossBar.Color) {
+ listener.send(ClientboundBossEventPacket.createRemovePacket(uuid))
+ dummy.dummyBarsUUID.forEach {
+ listener.send(ClientboundBossEventPacket.createRemovePacket(it))
+ }
+ dummy = PlayerDummyBossBar(color)
+ dummy.dummyBars.forEach {
+ listener.send(ClientboundBossEventPacket.createAddPacket(it))
+ }
+ listener.send(ClientboundBossEventPacket.createAddPacket(last))
+ }
+
+ fun remove() {
+ val channel = getConnection(listener).channel
+ channel.eventLoop().submit {
+ channel.pipeline().remove(INJECT_NAME)
+ }
+ listener.send(ClientboundBossEventPacket.createRemovePacket(uuid))
+ dummy.dummyBarsUUID.forEach {
+ listener.send(ClientboundBossEventPacket.createRemovePacket(it))
+ }
+ }
+
+ private fun writeBossBar(buf: HudByteBuf, ctx: ChannelHandlerContext?, msg: Any?, promise: ChannelPromise?) {
+ val originalUUID = buf.readUUID()
+ if (originalUUID == uuid || dummy.dummyBarsUUID.contains(originalUUID)) {
+ super.write(ctx, msg, promise)
+ return
+ }
+ if (BetterHudAPI.inst().isOnReload) return
+ val enum = buf.readEnum(operation)
+
+ fun getBuf(targetUUID: UUID = uuid) = HudByteBuf(Unpooled.buffer(1 shl 4))
+ .writeUUID(targetUUID)
+
+ fun sendProgress(getBuf: HudByteBuf = getBuf(), targetBuf: HudByteBuf = buf) = listener.send(createBossBar(getBuf
+ .writeEnum(operationEnum[2])
+ .writeFloat(targetBuf.readFloat())
+ ))
+ fun sendName(getBuf: HudByteBuf = getBuf(), targetBuf: HudByteBuf = buf) = listener.send(createBossBar(getBuf
+ .writeEnum(operationEnum[3])
+ .writeComponent(targetBuf.readComponentTrusted())
+ ))
+ fun sendStyle(getBuf: HudByteBuf = getBuf(), targetBuf: HudByteBuf = buf) = listener.send(createBossBar(getBuf
+ .writeEnum(operationEnum[4])
+ .writeEnum(targetBuf.readEnum(BossEvent.BossBarColor::class.java))
+ .writeEnum(targetBuf.readEnum(BossEvent.BossBarOverlay::class.java)))
+ )
+ fun sendProperties(getBuf: HudByteBuf = getBuf(), targetBuf: HudByteBuf = buf) = listener.send(createBossBar(getBuf
+ .writeEnum(operationEnum[5])
+ .writeByte(targetBuf.readUnsignedByte().toInt())
+ ))
+ fun changeName(targetBuf: HudByteBuf = buf) {
+ runCatching {
+ val hud = BetterHudAPI.inst().playerManager.getHudPlayer(player.uniqueId) ?: return
+ val comp = toAdventure(targetBuf.readComponentTrusted())
+ val key = BetterHudAPI.inst().defaultKey
+ fun applyFont(component: Component): Component {
+ return component.font(key).children(component.children().map {
+ applyFont(it)
+ })
+ }
+ fun hasDecoration(parent: Boolean, state: TextDecoration.State) = when (state) {
+ TextDecoration.State.TRUE -> true
+ TextDecoration.State.NOT_SET -> parent
+ TextDecoration.State.FALSE -> false
+ }
+ @Suppress("DEPRECATION")
+ fun getWidth(component: Component, bold: Boolean, italic: Boolean): Int {
+ var i = 0
+ if (bold) i++
+ if (italic) i++
+ return component.children().sumOf {
+ getWidth(
+ it,
+ hasDecoration(bold, it.decoration(TextDecoration.BOLD)),
+ hasDecoration(italic, it.decoration(TextDecoration.ITALIC))
+ )
+ } + (when (component) {
+ is TextComponent -> component.content()
+ is TranslatableComponent -> BetterHudAPI.inst().translate(player.locale, component.key())
+ else -> null
+ }?.codePoints()?.map {
+ (if (it == ' '.code) 4 else BetterHudAPI.inst().getWidth(it) + 1) + i
+ }?.sum() ?: 0)
+ }
+ hud.additionalComponent = WidthComponent(Component.text().append(applyFont(comp)), getWidth(
+ comp,
+ hasDecoration(false, comp.decoration(TextDecoration.BOLD)),
+ hasDecoration(false, comp.decoration(TextDecoration.ITALIC))
+ ))
+ }
+ }
+ fun removeBossbar(changeCache: Boolean = false): Boolean {
+ if (onUse.first == uuid) return false
+ var result = false
+ if (changeCache) {
+ val cacheSize = dummyBarHandleMap.size
+ if (cacheSize < dummy.line) {
+ val cache = CachedHudBossbar(dummy.dummyBars[cacheSize], onUse.first, HudByteBuf(onUse.second.unwrap()))
+ dummyBarHandleMap[onUse.first] = cache
+ sendName(getBuf = getBuf(cache.hud.uuid), targetBuf = onUse.second)
+ sendProgress(getBuf = getBuf(cache.hud.uuid), targetBuf = onUse.second)
+ sendStyle(getBuf = getBuf(cache.hud.uuid), targetBuf = onUse.second)
+ sendProperties(getBuf = getBuf(cache.hud.uuid), targetBuf = onUse.second)
+ result = true
+ }
+ }
+ otherBarCache.poll()?.let { target ->
+ val targetBuf = HudByteBuf(Unpooled.copiedBuffer(target.second.unwrap()))
+ listener.send(ClientboundBossEventPacket.createRemovePacket(target.first))
+ changeName(targetBuf = targetBuf)
+ sendProgress(targetBuf = targetBuf)
+ sendStyle(targetBuf = targetBuf)
+ sendProperties(targetBuf = targetBuf)
+ onUse = target
+ } ?: run {
+ onUse = uuid to HudByteBuf(buf.unwrap())
+ BetterHudAPI.inst().playerManager.getHudPlayer(player.uniqueId)?.additionalComponent = null
+ listener.send(ClientboundBossEventPacket.createUpdateNamePacket(last))
+ listener.send(ClientboundBossEventPacket.createUpdateProgressPacket(last))
+ listener.send(ClientboundBossEventPacket.createUpdateStylePacket(last))
+ listener.send(ClientboundBossEventPacket.createUpdatePropertiesPacket(last))
+ }
+ return result
+ }
+
+ runCatching {
+ val cacheSize = dummyBarHandleMap.size
+ if (cacheSize < dummy.line && enum.ordinal == 0) {
+ val hud = dummyBarHandleMap.computeIfAbsent(originalUUID) {
+ CachedHudBossbar(dummy.dummyBars[cacheSize], originalUUID, HudByteBuf(buf.unwrap()))
+ }
+ sendName(getBuf = getBuf(hud.hud.uuid))
+ sendProgress(getBuf = getBuf(hud.hud.uuid))
+ sendStyle(getBuf = getBuf(hud.hud.uuid))
+ sendProperties(getBuf = getBuf(hud.hud.uuid))
+ return
+ } else {
+ dummyBarHandleMap[originalUUID]?.let {
+ when (enum.ordinal) {
+ 0 -> {
+ sendName(getBuf = getBuf(it.hud.uuid))
+ sendProgress(getBuf = getBuf(it.hud.uuid))
+ sendStyle(getBuf = getBuf(it.hud.uuid))
+ sendProperties(getBuf = getBuf(it.hud.uuid))
+ }
+ 1 -> {
+ dummyBarHandleMap.remove(originalUUID)
+ val swap = removeBossbar(changeCache = true)
+ val list = dummyBarHandleMap.entries.toList()
+ val last = if (list.isNotEmpty()) list.last().value else it
+ list.forEachIndexed { index, target ->
+ val after = target.value
+ val targetBuf = after.buf
+ val newCache = CachedHudBossbar(dummy.dummyBars[index], after.cacheUUID, HudByteBuf(targetBuf.unwrap()))
+ target.setValue(newCache)
+ sendName(getBuf = getBuf(newCache.hud.uuid), targetBuf = targetBuf)
+ sendProgress(getBuf = getBuf(newCache.hud.uuid), targetBuf = targetBuf)
+ sendStyle(getBuf = getBuf(newCache.hud.uuid), targetBuf = targetBuf)
+ sendProperties(getBuf = getBuf(newCache.hud.uuid), targetBuf = targetBuf)
+ }
+ if (!swap) {
+ listener.send(ClientboundBossEventPacket.createUpdateNamePacket(last.hud))
+ listener.send(ClientboundBossEventPacket.createUpdateProgressPacket(last.hud))
+ listener.send(ClientboundBossEventPacket.createUpdateStylePacket(last.hud))
+ listener.send(ClientboundBossEventPacket.createUpdatePropertiesPacket(last.hud))
+ }
+ }
+ 2 -> sendProgress(getBuf = getBuf(it.hud.uuid))
+ 3 -> sendName(getBuf = getBuf(it.hud.uuid))
+ 4 -> sendStyle(getBuf = getBuf(it.hud.uuid))
+ 5 -> sendProperties(getBuf = getBuf(it.hud.uuid))
+ else -> {}
+ }
+ return
+ }
+ }
+ if (otherBarCache.isEmpty() && enum.ordinal == 0 && onUse.first == uuid) {
+ onUse = originalUUID to HudByteBuf(buf.unwrap())
+ changeName()
+ sendProgress()
+ sendStyle()
+ sendProperties()
+ return
+ }
+ if (originalUUID == onUse.first) {
+ when (enum.ordinal) {
+ 0 -> {
+ changeName()
+ sendProgress()
+ sendStyle()
+ sendProperties()
+ }
+ 1 -> removeBossbar()
+ 2 -> sendProgress()
+ 3 -> changeName()
+ 4 -> sendStyle()
+ 5 -> sendProperties()
+ else -> {}
+ }
+ } else {
+ when (enum.ordinal) {
+ 0 -> {
+ otherBarCache.removeIf {
+ it.first == originalUUID
+ }
+ otherBarCache.add(originalUUID to HudByteBuf(buf.unwrap()))
+ }
+ 1 -> otherBarCache.removeIf {
+ it.first == originalUUID
+ }
+ }
+ super.write(ctx, msg, promise)
+ }
+ }.onFailure {
+ it.printStackTrace()
+ }
+ }
+
+ override fun write(ctx: ChannelHandlerContext?, msg: Any?, promise: ChannelPromise?) {
+ if (msg is ClientboundBossEventPacket) {
+
+ if (BetterHudAPI.inst().isMergeBossBar) {
+ val buf = HudByteBuf(Unpooled.buffer(1 shl 4)).apply {
+ ClientboundBossEventPacket.STREAM_CODEC.encode(this, msg)
+ }
+ writeBossBar(buf, ctx, msg, promise)
+ } else super.write(ctx, msg, promise)
+ } else {
+ super.write(ctx, msg, promise)
+ }
+ }
+ }
+ private class HudByteBuf(private val source: ByteBuf) : RegistryFriendlyByteBuf(source, RegistryAccess.EMPTY) {
+ override fun unwrap(): ByteBuf {
+ return Unpooled.copiedBuffer(source)
+ }
+ override fun writeEnum(instance: Enum<*>): HudByteBuf {
+ super.writeEnum(instance)
+ return this
+ }
+ override fun writeUUID(uuid: UUID): HudByteBuf {
+ super.writeUUID(uuid)
+ return this
+ }
+ override fun writeFloat(f: Float): HudByteBuf {
+ super.writeFloat(f)
+ return this
+ }
+ override fun writeByte(i: Int): HudByteBuf {
+ super.writeByte(i)
+ return this
+ }
+ fun readComponentTrusted(): net.minecraft.network.chat.Component {
+ return ComponentSerialization.CODEC.parse(NbtOps.INSTANCE, readNbt(NbtAccounter.unlimitedHeap())).orThrow
+ }
+ fun writeComponent(component: net.minecraft.network.chat.Component): HudByteBuf {
+ writeNbt(ComponentSerialization.CODEC.encodeStart(NbtOps.INSTANCE, component).orThrow)
+ return this
+ }
+ }
+
+ private class HudBossBar(val uuid: UUID, component: net.minecraft.network.chat.Component, color: BossBarColor): BossEvent(uuid, component, color, BossBarOverlay.PROGRESS) {
+ constructor(uuid: UUID, component: Component, color: BossBar.Color): this(
+ uuid,
+ fromAdventure(component),
+ getColor(color)
+ )
+ override fun getProgress(): Float {
+ return 0F
+ }
+ }
+}
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index d12beea3..1de1acbb 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -33,6 +33,7 @@ include(
"nms:v1_20_R4",
"nms:v1_21_R1",
"nms:v1_21_R2",
+ "nms:v1_21_R3",
"scheduler:standard",
"scheduler:folia",