diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardHUDElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardHUDElement.kt index d12f041e7..3c03eaa24 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardHUDElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardHUDElement.kt @@ -2,6 +2,7 @@ package de.bixilon.minosoft.gui.rendering.gui.hud.elements.scoreboard import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.scoreboard.ScoreboardPositions +import de.bixilon.minosoft.gui.rendering.Drawable import de.bixilon.minosoft.gui.rendering.gui.hud.HUDRenderer import de.bixilon.minosoft.gui.rendering.gui.hud.elements.HUDBuilder import de.bixilon.minosoft.gui.rendering.gui.hud.elements.LayoutedHUDElement @@ -11,7 +12,7 @@ import de.bixilon.minosoft.modding.event.invoker.CallbackEventInvoker import de.bixilon.minosoft.util.KUtil.toResourceLocation import glm_.vec2.Vec2i -class ScoreboardHUDElement(hudRenderer: HUDRenderer) : LayoutedHUDElement(hudRenderer) { +class ScoreboardHUDElement(hudRenderer: HUDRenderer) : LayoutedHUDElement(hudRenderer), Drawable { private val connection = hudRenderer.connection override val layout = ScoreboardSideElement(hudRenderer) @@ -59,6 +60,13 @@ class ScoreboardHUDElement(hudRenderer: HUDRenderer) : LayoutedHUDElement { override val RESOURCE_LOCATION: ResourceLocation = "minosoft:scoreboard".toResourceLocation() diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardSideElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardSideElement.kt index f5c396697..6a7fb6bb9 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardSideElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardSideElement.kt @@ -40,7 +40,6 @@ class ScoreboardSideElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { } override fun forceRender(offset: Vec2i, z: Int, consumer: GUIVertexConsumer, options: GUIVertexOptions?): Int { - recalculateSize() backgroundElement.render(offset, z, consumer, options) nameBackgroundElement.render(offset, z + 1, consumer, options) @@ -75,7 +74,7 @@ class ScoreboardSideElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { queueSizeRecalculation() } - private fun recalculateSize() { + fun recalculateSize() { val size = Vec2i(MIN_WIDTH, Font.TOTAL_CHAR_HEIGHT) size.x = maxOf(size.x, nameElement.size.x) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/tab/TabListElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/tab/TabListElement.kt index 2ee654f13..3e465676f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/tab/TabListElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/tab/TabListElement.kt @@ -28,8 +28,8 @@ import de.bixilon.minosoft.util.KUtil.decide import de.bixilon.minosoft.util.KUtil.synchronizedMapOf import de.bixilon.minosoft.util.KUtil.toSynchronizedMap import glm_.vec2.Vec2i -import java.lang.Integer.max import java.util.* +import java.util.concurrent.locks.ReentrantLock class TabListElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { val header = TextElement(hudRenderer, "", background = false, fontAlignment = HorizontalAlignments.CENTER, parent = this) @@ -38,8 +38,11 @@ class TabListElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { private val background = ColorElement(hudRenderer, Vec2i.EMPTY, color = RGBColor(0, 0, 0, 120)) private var entriesSize = Vec2i.EMPTY - val entries: MutableMap = synchronizedMapOf() + private val entries: MutableMap = synchronizedMapOf() private var toRender: List = listOf() + private val lock = ReentrantLock() + var needsApply = false + private set val pingBarsAtlasElements: Array = arrayOf( hudRenderer.atlasManager["minecraft:tab_list_ping_0"]!!, @@ -57,7 +60,6 @@ class TabListElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { val size = size header.size.let { - header.silentApply() header.render(offset + Vec2i(HorizontalAlignments.CENTER.getOffset(size.x, it.x), 0), z, consumer, options) offset.y += it.y } @@ -96,15 +98,13 @@ class TabListElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { size.y += header.size.y - val toRender: MutableList = mutableListOf() + var toRender: MutableList = mutableListOf() + toRender += entries.toSynchronizedMap().values - // ToDo: Sorting isn't working correct: java.lang.IllegalArgumentException: Comparison method violates its general contract! - // Probably a threading issue - val tabListItems = hudRenderer.connection.tabList.tabListItemsByUUID.toSynchronizedMap().entries.sortedWith { a, b -> a.value.compareTo(b.value) } val previousSize = Vec2i(size) - var columns = tabListItems.size / ENTRIES_PER_COLUMN - if (tabListItems.size % ENTRIES_PER_COLUMN > 0) { + var columns = toRender.size / ENTRIES_PER_COLUMN + if (toRender.size % ENTRIES_PER_COLUMN > 0) { columns++ } @@ -113,32 +113,47 @@ class TabListElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { var currentMaxPrefWidth = 0 var totalEntriesWidth = 0 + + // ToDo: Still sometimes crashing + lock.lock() + try { + toRender.sort() + } catch (exception: Exception) { + exception.printStackTrace() + } + lock.unlock() + // Minecraft limits it to 80 items. Imho this is removing a feature, but some servers use a custom tab list plugin and then players are duplicated, etc + // ToDo: Detect custom tab list, e.g. check player names for non valid chars, etc + toRender = toRender.subList(0, minOf(toRender.size, MAX_ENTRIES)) + // Check width - var index = 0 - for ((uuid, item) in tabListItems) { - val entry = entries.getOrPut(uuid) { TabListEntryElement(hudRenderer, this, item, 0) } - toRender += entry + for ((index, entry) in toRender.withIndex()) { val prefWidth = entry.prefSize - currentMaxPrefWidth = max(currentMaxPrefWidth, prefWidth.x) + currentMaxPrefWidth = maxOf(currentMaxPrefWidth, prefWidth.x) if ((index + 1) % ENTRIES_PER_COLUMN == 0) { widths[column] = currentMaxPrefWidth totalEntriesWidth += currentMaxPrefWidth currentMaxPrefWidth = 0 column++ } - index++ } if (currentMaxPrefWidth != 0) { widths[column] = currentMaxPrefWidth totalEntriesWidth += currentMaxPrefWidth } - size.x = max(size.x, totalEntriesWidth) size.y += (columns > 1).decide(ENTRIES_PER_COLUMN, toRender.size) * (TabListEntryElement.HEIGHT + ENTRY_VERTICAL_SPACING) size.y -= ENTRY_VERTICAL_SPACING // Remove already added space again + + // add horizontal spacing to columns + if (columns >= 2) { + totalEntriesWidth += (columns - 1) * ENTRY_HORIZONTAL_SPACING + } + this.entriesSize = Vec2i(totalEntriesWidth, size.y - previousSize.y) + size.x = maxOf(size.x, totalEntriesWidth) // apply width to every cell @@ -150,21 +165,18 @@ class TabListElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { } } - if (columns >= 2) { - size.x += (columns - 1) * ENTRY_HORIZONTAL_SPACING - } - this.toRender = toRender size.y += footer.size.y - size.x = max(max(size.x, header.size.x), footer.size.x) + size.x = maxOf(size.x, header.size.x, footer.size.x) - this.size = size + this.size = size + 2 * BACKGROUND_PADDING - background.size = size + 2 * BACKGROUND_PADDING + background.size = size cacheUpToDate = false + needsApply = false } override fun silentApply(): Boolean { @@ -175,11 +187,30 @@ class TabListElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { return true } + fun update(uuid: UUID) { + val item = hudRenderer.connection.tabList.tabListItemsByUUID[uuid] ?: return + val entry = entries.getOrPut(uuid) { TabListEntryElement(hudRenderer, this, item, 0) } + lock.lock() + entry.silentApply() + lock.unlock() + needsApply = true + } + + fun remove(uuid: UUID) { + entries -= uuid + needsApply = true + } + + override fun onChildChange(child: Element) { + needsApply = true + } + companion object { private const val ENTRIES_PER_COLUMN = 20 private const val ENTRY_HORIZONTAL_SPACING = 5 private const val ENTRY_VERTICAL_SPACING = 1 - private const val BACKGROUND_PADDING = 1 + private const val BACKGROUND_PADDING = 3 + private const val MAX_ENTRIES = 80 } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/tab/TabListEntryElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/tab/TabListEntryElement.kt index 3acb5e6a3..0f4c2dd01 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/tab/TabListEntryElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/tab/TabListEntryElement.kt @@ -13,6 +13,7 @@ package de.bixilon.minosoft.gui.rendering.gui.hud.elements.tab +import de.bixilon.minosoft.data.abilities.Gamemodes import de.bixilon.minosoft.data.player.tab.TabListItem import de.bixilon.minosoft.data.text.ChatComponent import de.bixilon.minosoft.data.text.RGBColor @@ -27,6 +28,7 @@ import de.bixilon.minosoft.gui.rendering.gui.hud.HUDRenderer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions import de.bixilon.minosoft.gui.rendering.util.vec.Vec2Util.EMPTY +import de.bixilon.minosoft.util.KUtil.nullCompare import glm_.vec2.Vec2i import java.lang.Integer.max @@ -35,7 +37,7 @@ class TabListEntryElement( val tabList: TabListElement, val item: TabListItem, width: Int, -) : Element(hudRenderer), Pollable { +) : Element(hudRenderer), Pollable, Comparable { init { _parent = tabList } @@ -46,8 +48,11 @@ class TabListEntryElement( private val nameElement = TextElement(hudRenderer, "", background = false, parent = this) private lateinit var pingElement: ImageElement - private var displayName: ChatComponent = ChatComponent.EMPTY - private var ping = -1 + private var displayName: ChatComponent = item.displayName + private var ping = item.ping + private var gamemode: Gamemodes = item.gamemode + private var name: String = item.name + private var teamName = item.team?.name override var prefSize: Vec2i = Vec2i.EMPTY override var prefMaxSize: Vec2i @@ -99,19 +104,45 @@ class TabListEntryElement( } override fun poll(): Boolean { - val ping = item.ping val displayName = item.tabDisplayName + val ping = item.ping + val gamemode = item.gamemode + val name = item.name + val teamName = item.team?.name - if (this.ping == ping && this.displayName == displayName) { + if (this.ping == ping && this.displayName == displayName && this.gamemode == gamemode && this.name == name && this.teamName == teamName) { return false } this.ping = ping this.displayName = displayName + this.gamemode = gamemode + this.name = name + this.teamName = teamName return true } + override fun compareTo(other: TabListEntryElement): Int { + if (this.gamemode != other.gamemode) { + if (this.gamemode == Gamemodes.SPECTATOR) { + return -1 + } + if (other.gamemode == Gamemodes.SPECTATOR) { + return 1 + } + } + + this.teamName?.nullCompare(other.teamName)?.let { return it } + + this.name.lowercase().nullCompare(other.name.lowercase())?.let { return it } + + return 0 + } + + override fun toString(): String { + return displayName.legacyText + } companion object { const val HEIGHT = 10 diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/tab/TabListHUDElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/tab/TabListHUDElement.kt index 24ff5442d..ef2a7c182 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/tab/TabListHUDElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/tab/TabListHUDElement.kt @@ -17,6 +17,7 @@ import de.bixilon.minosoft.config.key.KeyAction import de.bixilon.minosoft.config.key.KeyBinding import de.bixilon.minosoft.config.key.KeyCodes import de.bixilon.minosoft.data.registries.ResourceLocation +import de.bixilon.minosoft.gui.rendering.Drawable import de.bixilon.minosoft.gui.rendering.gui.hud.HUDRenderer import de.bixilon.minosoft.gui.rendering.gui.hud.elements.HUDBuilder import de.bixilon.minosoft.gui.rendering.gui.hud.elements.LayoutedHUDElement @@ -26,11 +27,10 @@ import de.bixilon.minosoft.modding.event.invoker.CallbackEventInvoker import de.bixilon.minosoft.util.KUtil.toResourceLocation import glm_.vec2.Vec2i -class TabListHUDElement(hudRenderer: HUDRenderer) : LayoutedHUDElement(hudRenderer) { +class TabListHUDElement(hudRenderer: HUDRenderer) : LayoutedHUDElement(hudRenderer), Drawable { private val connection = renderWindow.connection override val layout = TabListElement(hudRenderer) - override val layoutOffset: Vec2i get() = Vec2i((hudRenderer.scaledSize.x - layout.size.x) / 2, 20) @@ -44,19 +44,15 @@ class TabListHUDElement(hudRenderer: HUDRenderer) : LayoutedHUDElement { layout.header.text = it.header layout.footer.text = it.footer - layout.forceApply() }) connection.registerEvent(CallbackEventInvoker.of { for ((uuid, entry) in it.items) { if (entry.remove) { - layout.entries.remove(uuid) + layout.remove(uuid) continue } - val element = layout.entries[uuid] ?: continue - element.forceApply() + layout.update(uuid) } - // ToDo: Cache more? - layout.forceApply() }) // ToDo: Also check team changes, scoreboard changes, etc @@ -64,6 +60,13 @@ class TabListHUDElement(hudRenderer: HUDRenderer) : LayoutedHUDElement { override val RESOURCE_LOCATION: ResourceLocation = "minosoft:tab_list".toResourceLocation()