ton of tab list fixes

This commit is contained in:
Bixilon 2021-11-03 16:38:58 +01:00
parent c84619ffb3
commit 05f9026ac9
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
5 changed files with 112 additions and 40 deletions

View File

@ -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<ScoreboardSideElement>(hudRenderer) {
class ScoreboardHUDElement(hudRenderer: HUDRenderer) : LayoutedHUDElement<ScoreboardSideElement>(hudRenderer), Drawable {
private val connection = hudRenderer.connection
override val layout = ScoreboardSideElement(hudRenderer)
@ -59,6 +60,13 @@ class ScoreboardHUDElement(hudRenderer: HUDRenderer) : LayoutedHUDElement<Scoreb
})
}
override fun draw() {
// check if content was changed, and we need to re-prepare before drawing
if (!layout.cacheUpToDate) {
layout.recalculateSize()
}
}
companion object : HUDBuilder<ScoreboardHUDElement> {
override val RESOURCE_LOCATION: ResourceLocation = "minosoft:scoreboard".toResourceLocation()

View File

@ -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)

View File

@ -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<UUID, TabListEntryElement> = synchronizedMapOf()
private val entries: MutableMap<UUID, TabListEntryElement> = synchronizedMapOf()
private var toRender: List<TabListEntryElement> = listOf()
private val lock = ReentrantLock()
var needsApply = false
private set
val pingBarsAtlasElements: Array<HUDAtlasElement> = 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<TabListEntryElement> = mutableListOf()
var toRender: MutableList<TabListEntryElement> = 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
}
}

View File

@ -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<TabListEntryElement> {
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

View File

@ -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<TabListElement>(hudRenderer) {
class TabListHUDElement(hudRenderer: HUDRenderer) : LayoutedHUDElement<TabListElement>(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<TabListEl
connection.registerEvent(CallbackEventInvoker.of<TabListInfoChangeEvent> {
layout.header.text = it.header
layout.footer.text = it.footer
layout.forceApply()
})
connection.registerEvent(CallbackEventInvoker.of<TabListEntryChangeEvent> {
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<TabListEl
// ToDo: Just forceApply when visible
}
override fun draw() {
// check if content was changed, and we need to re-prepare before drawing
if (layout.needsApply) {
layout.forceApply()
}
}
companion object : HUDBuilder<TabListHUDElement> {
override val RESOURCE_LOCATION: ResourceLocation = "minosoft:tab_list".toResourceLocation()