mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-15 10:25:06 -04:00
wip: hud tab list
This commit is contained in:
parent
aa7f5ca36e
commit
c143c52e22
@ -19,4 +19,8 @@ class PlayerProperty(
|
||||
) {
|
||||
val isSigned: Boolean
|
||||
get() = signature != null // ToDo check signature
|
||||
|
||||
override fun toString(): String {
|
||||
return "$key: $value"
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,8 @@ import de.bixilon.minosoft.data.text.ChatComponent
|
||||
import java.util.*
|
||||
|
||||
class TabList {
|
||||
val tabListItems: MutableMap<UUID, TabListItem> = mutableMapOf()
|
||||
val tabListItemsByUUID: MutableMap<UUID, TabListItem> = mutableMapOf()
|
||||
val tabListItemsByName: MutableMap<String, TabListItem> = mutableMapOf()
|
||||
var header = ChatComponent.of("")
|
||||
var footer = ChatComponent.of("")
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ package de.bixilon.minosoft.data.player.tab
|
||||
|
||||
import de.bixilon.minosoft.data.abilities.Gamemodes
|
||||
import de.bixilon.minosoft.data.player.PlayerProperty
|
||||
import de.bixilon.minosoft.data.scoreboard.Team
|
||||
import de.bixilon.minosoft.data.text.ChatComponent
|
||||
|
||||
data class TabListItem(
|
||||
@ -23,6 +24,7 @@ data class TabListItem(
|
||||
var gamemode: Gamemodes = Gamemodes.SURVIVAL,
|
||||
var displayName: ChatComponent = ChatComponent.of(name),
|
||||
var properties: Map<String, PlayerProperty> = mutableMapOf(),
|
||||
var team: Team? = null,
|
||||
) {
|
||||
|
||||
fun merge(data: TabListItemData) {
|
||||
@ -42,5 +44,10 @@ data class TabListItem(
|
||||
}
|
||||
}
|
||||
data.properties?.let { properties = it }
|
||||
|
||||
if (data.removeFromTeam) {
|
||||
this.team = null
|
||||
}
|
||||
data.team?.let { team = it }
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ package de.bixilon.minosoft.data.player.tab
|
||||
|
||||
import de.bixilon.minosoft.data.abilities.Gamemodes
|
||||
import de.bixilon.minosoft.data.player.PlayerProperty
|
||||
import de.bixilon.minosoft.data.scoreboard.Team
|
||||
import de.bixilon.minosoft.data.text.ChatComponent
|
||||
|
||||
data class TabListItemData(
|
||||
@ -25,4 +26,6 @@ data class TabListItemData(
|
||||
var displayName: ChatComponent? = null,
|
||||
val properties: Map<String, PlayerProperty>? = null,
|
||||
var remove: Boolean = false, // used for legacy tab list
|
||||
var team: Team? = null,
|
||||
var removeFromTeam: Boolean = false,
|
||||
)
|
||||
|
@ -15,7 +15,7 @@ package de.bixilon.minosoft.data.scoreboard
|
||||
import de.bixilon.minosoft.data.text.ChatCode
|
||||
import de.bixilon.minosoft.data.text.ChatComponent
|
||||
|
||||
class Team(
|
||||
data class Team(
|
||||
val name: String,
|
||||
var displayName: ChatComponent,
|
||||
var prefix: ChatComponent,
|
||||
@ -26,4 +26,8 @@ class Team(
|
||||
var nameTagVisibility: NameTagVisibilities,
|
||||
var formattingCode: ChatCode?,
|
||||
val members: MutableSet<String>,
|
||||
)
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,13 @@ open class ImageElement(
|
||||
super.size = value
|
||||
cacheUpToDate = false
|
||||
}
|
||||
|
||||
override var prefSize: Vec2i
|
||||
get() = size
|
||||
set(value) {
|
||||
size = value
|
||||
}
|
||||
|
||||
var tint: RGBColor = tint
|
||||
set(value) {
|
||||
field = value
|
||||
|
@ -41,6 +41,7 @@ open class TextElement(
|
||||
private var previousOffset = Vec2i.EMPTY
|
||||
private var previousMatrix = hudRenderer.matrix
|
||||
|
||||
private var previousMaxSize = Vec2i.EMPTY
|
||||
private var preparedSize = Vec2i.EMPTY
|
||||
var renderInfo = TextRenderInfo()
|
||||
|
||||
@ -73,6 +74,7 @@ open class TextElement(
|
||||
}
|
||||
|
||||
override fun silentApply() {
|
||||
val maxSize = maxSize
|
||||
val size = Vec2i.EMPTY
|
||||
if (!emptyMessage) {
|
||||
val renderInfo = TextRenderInfo()
|
||||
@ -81,6 +83,8 @@ open class TextElement(
|
||||
this.renderInfo = renderInfo
|
||||
}
|
||||
|
||||
|
||||
this.previousMaxSize = maxSize
|
||||
this.cacheUpToDate = false
|
||||
this.size = size
|
||||
preparedSize = size
|
||||
@ -90,6 +94,10 @@ open class TextElement(
|
||||
|
||||
override fun onParentChange() {
|
||||
val maxSize = maxSize
|
||||
if (previousMaxSize == maxSize) {
|
||||
// no change in size
|
||||
return
|
||||
}
|
||||
val prefSize = prefSize
|
||||
|
||||
if (preparedSize.x < prefSize.x || preparedSize.x > maxSize.x) {
|
||||
|
@ -56,7 +56,7 @@ class TextFlowElement(
|
||||
yOffset += Font.TOTAL_CHAR_HEIGHT
|
||||
}
|
||||
|
||||
background.render(offset, z, consumer)
|
||||
background.render(Vec2i(offset), z, consumer)
|
||||
return LAYERS
|
||||
}
|
||||
|
||||
@ -159,6 +159,6 @@ class TextFlowElement(
|
||||
companion object {
|
||||
const val LAYERS = TextElement.LAYERS
|
||||
|
||||
const val MAX_TOTAL_MESSAGES = 500 // ToDo: Used for scrolling
|
||||
const val MAX_TOTAL_MESSAGES = 500
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import de.bixilon.minosoft.gui.rendering.Renderer
|
||||
import de.bixilon.minosoft.gui.rendering.RendererBuilder
|
||||
import de.bixilon.minosoft.gui.rendering.gui.hud.atlas.HUDAtlasManager
|
||||
import de.bixilon.minosoft.gui.rendering.gui.hud.elements.*
|
||||
import de.bixilon.minosoft.gui.rendering.gui.hud.elements.tab.TabListHUDElement
|
||||
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMesh
|
||||
import de.bixilon.minosoft.gui.rendering.modding.events.ResizeWindowEvent
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.Vec2Util.EMPTY
|
||||
@ -77,6 +78,7 @@ class HUDRenderer(
|
||||
if (Minosoft.config.config.game.hud.internalMessages.enabled) {
|
||||
registerElement(InternalMessagesHUDElement)
|
||||
}
|
||||
registerElement(TabListHUDElement)
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
|
@ -23,7 +23,7 @@ class HUDAtlasElement(
|
||||
val end: Vec2i,
|
||||
val slots: Map<Int, Vec2Binding<Int>>, // ToDo: Use an array?
|
||||
) : TextureLike {
|
||||
override val size: Vec2i = start - end
|
||||
override val size: Vec2i = end - start
|
||||
override lateinit var uvStart: Vec2
|
||||
override lateinit var uvEnd: Vec2
|
||||
}
|
||||
|
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2021 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.gui.hud.elements.tab
|
||||
|
||||
import de.bixilon.minosoft.data.abilities.Gamemodes
|
||||
import de.bixilon.minosoft.data.text.RGBColor
|
||||
import de.bixilon.minosoft.gui.rendering.gui.elements.Element
|
||||
import de.bixilon.minosoft.gui.rendering.gui.elements.ElementAlignments
|
||||
import de.bixilon.minosoft.gui.rendering.gui.elements.ElementAlignments.Companion.getOffset
|
||||
import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.ColorElement
|
||||
import de.bixilon.minosoft.gui.rendering.gui.elements.text.TextElement
|
||||
import de.bixilon.minosoft.gui.rendering.gui.hud.HUDRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.gui.hud.atlas.HUDAtlasElement
|
||||
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.Vec2Util.EMPTY
|
||||
import de.bixilon.minosoft.util.KUtil.decide
|
||||
import de.bixilon.minosoft.util.KUtil.nullCompare
|
||||
import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
||||
import de.bixilon.minosoft.util.KUtil.toSynchronizedMap
|
||||
import glm_.vec2.Vec2i
|
||||
import java.lang.Integer.max
|
||||
|
||||
class TabListElement(hudRenderer: HUDRenderer) : Element(hudRenderer) {
|
||||
val header = TextElement(hudRenderer, "", background = false, fontAlignment = ElementAlignments.CENTER, parent = this)
|
||||
val footer = TextElement(hudRenderer, "", background = false, fontAlignment = ElementAlignments.CENTER, parent = this)
|
||||
|
||||
private val background = ColorElement(hudRenderer, Vec2i.EMPTY, color = RGBColor(0, 0, 0, 120))
|
||||
private var entriesSize = Vec2i.EMPTY
|
||||
private var entries: List<TabListEntryElement> = listOf()
|
||||
|
||||
val PING_BARS: Array<HUDAtlasElement> = arrayOf(
|
||||
hudRenderer.atlasManager["minecraft:tab_list_ping_0".toResourceLocation()]!!,
|
||||
hudRenderer.atlasManager["minecraft:tab_list_ping_1".toResourceLocation()]!!,
|
||||
hudRenderer.atlasManager["minecraft:tab_list_ping_2".toResourceLocation()]!!,
|
||||
hudRenderer.atlasManager["minecraft:tab_list_ping_3".toResourceLocation()]!!,
|
||||
hudRenderer.atlasManager["minecraft:tab_list_ping_4".toResourceLocation()]!!,
|
||||
hudRenderer.atlasManager["minecraft:tab_list_ping_5".toResourceLocation()]!!,
|
||||
)
|
||||
|
||||
override fun render(offset: Vec2i, z: Int, consumer: GUIVertexConsumer): Int {
|
||||
silentApply()
|
||||
background.render(Vec2i(offset), z, consumer)
|
||||
val size = size
|
||||
|
||||
header.size.let {
|
||||
header.onParentChange()
|
||||
header.render(offset + Vec2i(ElementAlignments.CENTER.getOffset(size.x, it.x), 0), z, consumer)
|
||||
offset.y += it.y
|
||||
}
|
||||
|
||||
val offsetBefore = Vec2i(offset)
|
||||
offset.x += ElementAlignments.CENTER.getOffset(size.x, entriesSize.x)
|
||||
|
||||
var columns = entries.size / ENTRIES_PER_COLUMN
|
||||
if (entries.size % ENTRIES_PER_COLUMN > 0) {
|
||||
columns++
|
||||
}
|
||||
|
||||
for ((index, entry) in entries.withIndex()) {
|
||||
entry.render(Vec2i(offset), z + 1, consumer)
|
||||
offset.y += TabListEntryElement.HEIGHT + ENTRY_VERTICAL_SPACING
|
||||
if ((index + 1) % ENTRIES_PER_COLUMN == 0) {
|
||||
offset.x += entry.width + ENTRY_HORIZONTAL_SPACING
|
||||
offset.y = offsetBefore.y
|
||||
}
|
||||
}
|
||||
offset.x = offsetBefore.x
|
||||
offset.y = offsetBefore.y + (columns > 1).decide(ENTRIES_PER_COLUMN, entries.size) * (TabListEntryElement.HEIGHT + ENTRY_VERTICAL_SPACING)
|
||||
|
||||
|
||||
footer.size.let {
|
||||
footer.render(offset + Vec2i(ElementAlignments.CENTER.getOffset(size.x, it.x), 0), z, consumer)
|
||||
offset.y += it.y
|
||||
}
|
||||
|
||||
return TextElement.LAYERS + 1 // ToDo
|
||||
}
|
||||
|
||||
override fun silentApply() {
|
||||
val maxSize = maxSize
|
||||
val size = Vec2i.EMPTY
|
||||
|
||||
|
||||
header.onParentChange()
|
||||
footer.onParentChange()
|
||||
|
||||
size.y += header.size.y
|
||||
|
||||
val entries: MutableList<TabListEntryElement> = mutableListOf()
|
||||
|
||||
val tabListItems = hudRenderer.connection.tabList.tabListItemsByUUID.toSynchronizedMap().values.sortedWith { a, b ->
|
||||
if (a.gamemode != b.gamemode) {
|
||||
if (a.gamemode == Gamemodes.SPECTATOR) {
|
||||
return@sortedWith -1
|
||||
}
|
||||
if (b.gamemode == Gamemodes.SPECTATOR) {
|
||||
return@sortedWith 1
|
||||
}
|
||||
}
|
||||
|
||||
a.team?.name?.nullCompare(b.team?.name)?.let { return@sortedWith it }
|
||||
|
||||
a.name.nullCompare(b?.name)?.let { return@sortedWith it } // ToDo: Case?
|
||||
|
||||
return@sortedWith 0
|
||||
}
|
||||
|
||||
val previousSize = Vec2i(size)
|
||||
var columns = tabListItems.size / ENTRIES_PER_COLUMN
|
||||
if (tabListItems.size % ENTRIES_PER_COLUMN > 0) {
|
||||
columns++
|
||||
}
|
||||
|
||||
var column = 0
|
||||
val widths = IntArray(columns)
|
||||
var currentMaxPrefWidth = 0
|
||||
var totalEntriesWidth = 0
|
||||
|
||||
// Check width
|
||||
for ((index, item) in tabListItems.withIndex()) {
|
||||
val entry = TabListEntryElement(hudRenderer, this, item, 0)
|
||||
entries += entry
|
||||
val prefWidth = entry.prefSize
|
||||
|
||||
currentMaxPrefWidth = max(currentMaxPrefWidth, prefWidth.x)
|
||||
if ((index + 1) % ENTRIES_PER_COLUMN == 0) {
|
||||
widths[column] = currentMaxPrefWidth
|
||||
totalEntriesWidth += currentMaxPrefWidth
|
||||
currentMaxPrefWidth = 0
|
||||
column++
|
||||
}
|
||||
}
|
||||
if (currentMaxPrefWidth != 0) {
|
||||
widths[column] = currentMaxPrefWidth
|
||||
totalEntriesWidth += currentMaxPrefWidth
|
||||
}
|
||||
size.x = max(size.x, totalEntriesWidth)
|
||||
size.y += (columns > 1).decide(ENTRIES_PER_COLUMN, entries.size) * (TabListEntryElement.HEIGHT + ENTRY_VERTICAL_SPACING)
|
||||
|
||||
this.entriesSize = Vec2i(totalEntriesWidth, size.y - previousSize.y)
|
||||
|
||||
|
||||
// apply width to every cell
|
||||
column = 0
|
||||
for ((index, entry) in entries.withIndex()) {
|
||||
entry.width = widths[column]
|
||||
if ((index + 1) % ENTRIES_PER_COLUMN == 0) {
|
||||
column++
|
||||
}
|
||||
}
|
||||
|
||||
if (columns >= 2) {
|
||||
size.x += (columns - 1) * ENTRY_HORIZONTAL_SPACING
|
||||
}
|
||||
|
||||
this.entries = entries
|
||||
|
||||
size.y += footer.size.y
|
||||
|
||||
size.x = max(max(size.x, header.size.x), footer.size.x)
|
||||
|
||||
|
||||
this.size = size
|
||||
|
||||
background.size = size
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val ENTRIES_PER_COLUMN = 20
|
||||
private const val ENTRY_HORIZONTAL_SPACING = 5
|
||||
private const val ENTRY_VERTICAL_SPACING = 1
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2021 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.gui.hud.elements.tab
|
||||
|
||||
import de.bixilon.minosoft.data.player.tab.TabListItem
|
||||
import de.bixilon.minosoft.data.text.BaseComponent
|
||||
import de.bixilon.minosoft.data.text.ChatComponent
|
||||
import de.bixilon.minosoft.data.text.RGBColor
|
||||
import de.bixilon.minosoft.gui.rendering.gui.elements.Element
|
||||
import de.bixilon.minosoft.gui.rendering.gui.elements.ElementAlignments
|
||||
import de.bixilon.minosoft.gui.rendering.gui.elements.ElementAlignments.Companion.getOffset
|
||||
import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.ColorElement
|
||||
import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.ImageElement
|
||||
import de.bixilon.minosoft.gui.rendering.gui.elements.text.TextElement
|
||||
import de.bixilon.minosoft.gui.rendering.gui.hud.HUDRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.Vec2Util.EMPTY
|
||||
import glm_.vec2.Vec2i
|
||||
import java.lang.Integer.max
|
||||
|
||||
class TabListEntryElement(
|
||||
hudRenderer: HUDRenderer,
|
||||
val tabList: TabListElement,
|
||||
val item: TabListItem,
|
||||
width: Int,
|
||||
) : Element(hudRenderer) {
|
||||
// ToDo: Skin
|
||||
private val background: ColorElement
|
||||
|
||||
private val nameElement = TextElement(hudRenderer, "", background = false, parent = this)
|
||||
private lateinit var pingElement: ImageElement
|
||||
|
||||
private var lastDisplayName: ChatComponent? = null
|
||||
private var lastPing = -1
|
||||
|
||||
override var prefSize: Vec2i = Vec2i.EMPTY
|
||||
override var prefMaxSize: Vec2i
|
||||
get() = Vec2i(width, HEIGHT)
|
||||
set(value) {}
|
||||
override var size: Vec2i
|
||||
get() = maxSize
|
||||
set(value) {}
|
||||
|
||||
private var forcePrepare = true
|
||||
var width: Int = width
|
||||
set(value) {
|
||||
field = value
|
||||
forcePrepare = true
|
||||
apply()
|
||||
}
|
||||
|
||||
init {
|
||||
background = ColorElement(hudRenderer, size, RGBColor(80, 80, 80, 130))
|
||||
silentApply()
|
||||
}
|
||||
|
||||
override fun render(offset: Vec2i, z: Int, consumer: GUIVertexConsumer): Int {
|
||||
background.render(Vec2i(offset), z, consumer)
|
||||
nameElement.render(Vec2i(offset), z, consumer)
|
||||
pingElement.render(offset + Vec2i(ElementAlignments.RIGHT.getOffset(maxSize.x, pingElement.size.x + PADDING), PADDING), z + 1, consumer)
|
||||
|
||||
return TextElement.LAYERS
|
||||
}
|
||||
|
||||
override fun silentApply() {
|
||||
val ping = item.ping
|
||||
|
||||
if (forcePrepare || ping != lastPing) {
|
||||
pingElement = ImageElement(hudRenderer, tabList.PING_BARS[when {
|
||||
ping < 0 -> 0
|
||||
ping < 150 -> 5
|
||||
ping < 300 -> 4
|
||||
ping < 600 -> 3
|
||||
ping < 1000 -> 2
|
||||
else -> 1
|
||||
}])
|
||||
nameElement.prefMaxSize = Vec2i(max(0, maxSize.x - pingElement.size.x), HEIGHT)
|
||||
lastPing = ping
|
||||
}
|
||||
val displayName = BaseComponent()
|
||||
item.team?.prefix?.let {
|
||||
displayName += it
|
||||
}
|
||||
displayName += item.displayName.apply {
|
||||
// ToDo: Set correct formatting code
|
||||
val color = item.team?.formattingCode
|
||||
if (color is RGBColor) {
|
||||
applyDefaultColor(color)
|
||||
}
|
||||
}
|
||||
item.team?.suffix?.let {
|
||||
displayName += it
|
||||
}
|
||||
|
||||
if (forcePrepare || displayName !== lastDisplayName) {
|
||||
nameElement.text = displayName
|
||||
lastDisplayName = displayName
|
||||
}
|
||||
|
||||
this.prefSize = Vec2i((PADDING * 2) + nameElement.prefSize.x + INNER_MARGIN + pingElement.prefSize.x, HEIGHT)
|
||||
background.size = size
|
||||
forcePrepare = false
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
const val HEIGHT = 10
|
||||
const val INNER_MARGIN = 5
|
||||
const val PADDING = 1
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2021 Moritz Zwerger
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.gui.hud.elements.tab
|
||||
|
||||
import de.bixilon.minosoft.data.registries.ResourceLocation
|
||||
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.HUDElement
|
||||
import de.bixilon.minosoft.modding.event.events.TabListInfoChangeEvent
|
||||
import de.bixilon.minosoft.modding.event.invoker.CallbackEventInvoker
|
||||
import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
||||
import glm_.vec2.Vec2i
|
||||
|
||||
class TabListHUDElement(hudRenderer: HUDRenderer) : HUDElement<TabListElement>(hudRenderer) {
|
||||
private val connection = renderWindow.connection
|
||||
override val layout = TabListElement(hudRenderer)
|
||||
|
||||
|
||||
override val layoutOffset: Vec2i
|
||||
get() = Vec2i((hudRenderer.scaledSize.x - layout.size.x) / 2, 20)
|
||||
|
||||
init {
|
||||
layout.prefMaxSize = Vec2i(-1, -1)
|
||||
}
|
||||
|
||||
|
||||
override fun init() {
|
||||
connection.registerEvent(CallbackEventInvoker.of<TabListInfoChangeEvent> {
|
||||
layout.header.text = it.header
|
||||
layout.footer.text = it.footer
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
companion object : HUDBuilder<TabListHUDElement> {
|
||||
override val RESOURCE_LOCATION: ResourceLocation = "minosoft:tab_list".toResourceLocation()
|
||||
|
||||
override fun build(hudRenderer: HUDRenderer): TabListHUDElement {
|
||||
return TabListHUDElement(hudRenderer)
|
||||
}
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ import de.bixilon.minosoft.modding.event.events.connection.play.PlayConnectionEv
|
||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
||||
import de.bixilon.minosoft.protocol.packets.s2c.play.TabListTextSetS2CP
|
||||
|
||||
class PlayerListInfoChangeEvent(
|
||||
class TabListInfoChangeEvent(
|
||||
connection: PlayConnection,
|
||||
initiator: EventInitiators,
|
||||
val header: ChatComponent,
|
@ -86,7 +86,7 @@ class PlayerEntitySpawnS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
|
||||
}
|
||||
|
||||
override fun handle(connection: PlayConnection) {
|
||||
connection.tabList.tabListItems[entityUUID]?.let { entity.tabListItem = it }
|
||||
// connection.tabList.tabListItemsByUUID[entityUUID]?.let { entity.tabListItem = it }
|
||||
|
||||
connection.fireEvent(EntitySpawnEvent(connection, this))
|
||||
connection.world.entities.add(entityId, entityUUID, entity)
|
||||
|
@ -24,6 +24,7 @@ import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
|
||||
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
|
||||
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
|
||||
import de.bixilon.minosoft.util.KUtil
|
||||
import de.bixilon.minosoft.util.KUtil.toSynchronizedMap
|
||||
import de.bixilon.minosoft.util.enum.ValuesEnum
|
||||
import de.bixilon.minosoft.util.logging.Log
|
||||
import de.bixilon.minosoft.util.logging.LogLevels
|
||||
@ -123,36 +124,47 @@ class TabListDataS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
|
||||
if (connection.version.versionId < ProtocolVersions.V_14W19A) { // ToDo: 19?
|
||||
val item: TabListItem = if (data.remove) {
|
||||
// add or remove
|
||||
connection.tabList.tabListItems[uuid]?.apply {
|
||||
connection.tabList.tabListItems.remove(uuid)
|
||||
} ?: let {
|
||||
connection.tabList.tabListItemsByUUID[uuid]?.apply {
|
||||
connection.tabList.tabListItemsByUUID.remove(uuid)
|
||||
connection.tabList.tabListItemsByName.remove(data.name)
|
||||
} ?: TabListItem(name = data.name!!).apply {
|
||||
// add
|
||||
val itemToAdd = TabListItem(name = data.name!!)
|
||||
connection.tabList.tabListItems[uuid] = itemToAdd
|
||||
itemToAdd
|
||||
connection.tabList.tabListItemsByUUID[uuid] = this
|
||||
connection.tabList.tabListItemsByName[data.name] = this
|
||||
}
|
||||
} else {
|
||||
connection.tabList.tabListItems[uuid]!!
|
||||
connection.tabList.tabListItemsByUUID[uuid]!!
|
||||
}
|
||||
|
||||
item.merge(data)
|
||||
continue
|
||||
}
|
||||
if (data.remove) {
|
||||
connection.tabList.tabListItems.remove(uuid)
|
||||
val item = connection.tabList.tabListItemsByUUID.remove(uuid) ?: continue
|
||||
connection.tabList.tabListItemsByName.remove(item.name)
|
||||
continue
|
||||
}
|
||||
|
||||
val entity = connection.world.entities[uuid]
|
||||
|
||||
|
||||
val tabListItem = connection.tabList.tabListItems[uuid] ?: run {
|
||||
val tabListItem = connection.tabList.tabListItemsByUUID[uuid] ?: run {
|
||||
if (data.name == null) {
|
||||
// item not yet created
|
||||
return@run null
|
||||
}
|
||||
val item = TabListItem(name = data.name)
|
||||
connection.tabList.tabListItems[uuid] = item
|
||||
connection.tabList.tabListItemsByUUID[uuid] = item
|
||||
connection.tabList.tabListItemsByName[data.name] = item
|
||||
|
||||
// set team
|
||||
|
||||
for (team in connection.scoreboardManager.teams.toSynchronizedMap().values) {
|
||||
if (team.members.contains(data.name)) {
|
||||
item.team = team
|
||||
break
|
||||
}
|
||||
}
|
||||
item
|
||||
} ?: continue
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
package de.bixilon.minosoft.protocol.packets.s2c.play
|
||||
|
||||
import de.bixilon.minosoft.data.text.ChatComponent
|
||||
import de.bixilon.minosoft.modding.event.events.PlayerListInfoChangeEvent
|
||||
import de.bixilon.minosoft.modding.event.events.TabListInfoChangeEvent
|
||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
||||
import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket
|
||||
import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer
|
||||
@ -26,7 +26,7 @@ class TabListTextSetS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket() {
|
||||
val footer: ChatComponent = buffer.readChatComponent()
|
||||
|
||||
override fun handle(connection: PlayConnection) {
|
||||
if (connection.fireEvent(PlayerListInfoChangeEvent(connection, this))) {
|
||||
if (connection.fireEvent(TabListInfoChangeEvent(connection, this))) {
|
||||
return
|
||||
}
|
||||
connection.tabList.header = header
|
||||
|
@ -101,7 +101,7 @@ class TeamCreateS2CP(val name: String, buffer: PlayInByteBuffer) : PlayS2CPacket
|
||||
|
||||
|
||||
override fun handle(connection: PlayConnection) {
|
||||
connection.scoreboardManager.teams[name] = Team(
|
||||
val team = Team(
|
||||
name = name,
|
||||
displayName = displayName,
|
||||
prefix = prefix,
|
||||
@ -113,6 +113,11 @@ class TeamCreateS2CP(val name: String, buffer: PlayInByteBuffer) : PlayS2CPacket
|
||||
formattingCode = formattingCode,
|
||||
members = members.toMutableSet(),
|
||||
)
|
||||
connection.scoreboardManager.teams[name] = team
|
||||
|
||||
for (member in members) {
|
||||
connection.tabList.tabListItemsByName[member]?.team = team
|
||||
}
|
||||
}
|
||||
|
||||
override fun log() {
|
||||
|
@ -32,7 +32,12 @@ class TeamMemberAddS2CP(val name: String, buffer: PlayInByteBuffer) : PlayS2CPac
|
||||
|
||||
|
||||
override fun handle(connection: PlayConnection) {
|
||||
connection.scoreboardManager.teams[name]?.members?.addAll(members)
|
||||
val team = connection.scoreboardManager.teams[name] ?: return
|
||||
team.members += members
|
||||
|
||||
for (member in members) {
|
||||
connection.tabList.tabListItemsByName[member]?.team = team
|
||||
}
|
||||
}
|
||||
|
||||
override fun log() {
|
||||
|
@ -31,7 +31,16 @@ class TeamMemberRemoveS2CP(val name: String, buffer: PlayInByteBuffer) : PlayS2C
|
||||
|
||||
|
||||
override fun handle(connection: PlayConnection) {
|
||||
connection.scoreboardManager.teams[name]?.members?.removeAll(members)
|
||||
val team = connection.scoreboardManager.teams[name] ?: return
|
||||
team.members -= members
|
||||
|
||||
for (member in members) {
|
||||
val item = connection.tabList.tabListItemsByName[member] ?: continue
|
||||
if (item.team != team) {
|
||||
continue
|
||||
}
|
||||
item.team = team
|
||||
}
|
||||
}
|
||||
|
||||
override fun log() {
|
||||
|
@ -211,14 +211,14 @@ class PacketTypes {
|
||||
PLAY_COMBAT_EVENT_END({ CombatEventEndS2CP(it) }),
|
||||
PLAY_COMBAT_EVENT_ENTER({ CombatEventEnterS2CP() }),
|
||||
PLAY_COMBAT_EVENT_KILL({ CombatEventKillS2CP(it) }),
|
||||
PLAY_TAB_LIST_DATA({ TabListDataS2CP(it) }),
|
||||
PLAY_TAB_LIST_DATA({ TabListDataS2CP(it) }, isThreadSafe = false),
|
||||
PLAY_PLAYER_FACE({ PlayerFaceS2CP(it) }),
|
||||
PLAY_POSITION_AND_ROTATION({ PositionAndRotationS2CP(it) }),
|
||||
PLAY_UNLOCK_RECIPES({ PacketUnlockRecipes(it) }),
|
||||
PLAY_ENTITY_DESTROY({ EntityDestroyS2CP(it) }),
|
||||
PLAY_ENTITY_STATUS_EFFECT_REMOVE({ EntityStatusEffectRemoveS2CP(it) }),
|
||||
PLAY_RESOURCEPACK_REQUEST({ ResourcepackRequestS2CP(it) }),
|
||||
PLAY_RESPAWN({ RespawnS2CP(it) }, isThreadSafe = false),
|
||||
PLAY_RESPAWN({ RespawnS2CP(it) }),
|
||||
PLAY_ENTITY_HEAD_ROTATION({ EntityHeadRotationS2CP(it) }),
|
||||
PLAY_SELECT_ADVANCEMENT_TAB({ PacketSelectAdvancementTab(it) }),
|
||||
PLAY_WORLD_BORDER({ WorldBorderS2CF.createPacket(it) }),
|
||||
|
@ -31,7 +31,7 @@ public class CommandTabList extends Command {
|
||||
new CommandLiteralNode("list", (connection, stack) -> {
|
||||
print(connection.getTabList().getHeader().getAnsiColoredMessage());
|
||||
|
||||
int entries = connection.getTabList().getTabListItems().size();
|
||||
int entries = connection.getTabList().getTabListItemsByUUID().size();
|
||||
int columns = (entries / 20) + 1;
|
||||
if (columns > 4) {
|
||||
columns = 4;
|
||||
@ -43,12 +43,12 @@ public class CommandTabList extends Command {
|
||||
|
||||
ArrayList<Object[]> tableData = new ArrayList<>();
|
||||
|
||||
Iterator<TabListItem> playerListItems = connection.getTabList().getTabListItems().values().iterator();
|
||||
Iterator<TabListItem> tabListItemIterator = connection.getTabList().getTabListItemsByUUID().values().iterator();
|
||||
for (int row = 0; row < rows; row++) {
|
||||
ArrayList<Object> current = new ArrayList<>();
|
||||
for (int column = 0; column < columns; column++) {
|
||||
if (playerListItems.hasNext()) {
|
||||
current.add(playerListItems.next().getDisplayName());
|
||||
if (tabListItemIterator.hasNext()) {
|
||||
current.add(tabListItemIterator.next().getDisplayName());
|
||||
} else {
|
||||
current.add(null);
|
||||
}
|
||||
@ -68,7 +68,7 @@ public class CommandTabList extends Command {
|
||||
|
||||
ArrayList<Object[]> tableData = new ArrayList<>();
|
||||
|
||||
for (var entry : connection.getTabList().getTabListItems().entrySet()) {
|
||||
for (var entry : connection.getTabList().getTabListItemsByUUID().entrySet()) {
|
||||
PlayerEntity playerEntity = (PlayerEntity) connection.getWorld().getEntities().get(entry.getKey());
|
||||
Integer entityId = playerEntity != null ? connection.getWorld().getEntities().getId(playerEntity) : null;
|
||||
tableData.add(new Object[]{entry.getKey(), entityId, entry.getValue().getName(), entry.getValue().getDisplayName(), entry.getValue().getGamemode(), entry.getValue().getPing() + "ms"});
|
||||
|
@ -409,4 +409,14 @@ object KUtil {
|
||||
fun ByteArray.toBase64(): String {
|
||||
return Base64.getEncoder().encodeToString(this)
|
||||
}
|
||||
|
||||
|
||||
fun String?.nullCompare(other: String?): Int? {
|
||||
(this ?: "").compareTo(other ?: "").let {
|
||||
if (it != 0) {
|
||||
return it
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,8 @@ import de.bixilon.minosoft.config.StaticConfiguration
|
||||
import de.bixilon.minosoft.data.text.BaseComponent
|
||||
import de.bixilon.minosoft.data.text.ChatComponent
|
||||
import de.bixilon.minosoft.data.text.TextComponent
|
||||
import de.bixilon.minosoft.modding.event.events.InternalMessageReceiveEvent
|
||||
import de.bixilon.minosoft.terminal.CLI
|
||||
import de.bixilon.minosoft.terminal.RunConfiguration
|
||||
import java.io.PrintStream
|
||||
import java.io.PrintWriter
|
||||
@ -84,6 +86,9 @@ object Log {
|
||||
}
|
||||
|
||||
stream.println(message.ansiColoredMessage)
|
||||
|
||||
val cliConnection = CLI.getCurrentConnection()
|
||||
cliConnection?.fireEvent(InternalMessageReceiveEvent(cliConnection, messageToSend.message))
|
||||
} catch (exception: Throwable) {
|
||||
SYSTEM_ERR_STREAM.println("Can not send log message $messageToSend!")
|
||||
}
|
||||
|
@ -64,6 +64,48 @@
|
||||
"end": [16, 16]
|
||||
}
|
||||
},
|
||||
"minecraft:tab_list_ping_5": {
|
||||
"0": {
|
||||
"texture": "minecraft:textures/gui/icons.png",
|
||||
"start": [0, 16],
|
||||
"end": [10, 23]
|
||||
}
|
||||
},
|
||||
"minecraft:tab_list_ping_4": {
|
||||
"0": {
|
||||
"texture": "minecraft:textures/gui/icons.png",
|
||||
"start": [0, 24],
|
||||
"end": [10, 31]
|
||||
}
|
||||
},
|
||||
"minecraft:tab_list_ping_3": {
|
||||
"0": {
|
||||
"texture": "minecraft:textures/gui/icons.png",
|
||||
"start": [0, 32],
|
||||
"end": [10, 39]
|
||||
}
|
||||
},
|
||||
"minecraft:tab_list_ping_2": {
|
||||
"0": {
|
||||
"texture": "minecraft:textures/gui/icons.png",
|
||||
"start": [0, 40],
|
||||
"end": [10, 47]
|
||||
}
|
||||
},
|
||||
"minecraft:tab_list_ping_1": {
|
||||
"0": {
|
||||
"texture": "minecraft:textures/gui/icons.png",
|
||||
"start": [0, 48],
|
||||
"end": [10, 55]
|
||||
}
|
||||
},
|
||||
"minecraft:tab_list_ping_0": {
|
||||
"0": {
|
||||
"texture": "minecraft:textures/gui/icons.png",
|
||||
"start": [0, 56],
|
||||
"end": [10, 63]
|
||||
}
|
||||
},
|
||||
"minecraft:experience_bar_empty": {
|
||||
"0": {
|
||||
"texture": "minecraft:textures/gui/icons.png",
|
||||
|
Loading…
x
Reference in New Issue
Block a user