From 5f28cceb4953b29cdd7a91cf2090ae2a5cdcdf6a Mon Sep 17 00:00:00 2001 From: Bixilon Date: Tue, 2 Nov 2021 15:44:27 +0100 Subject: [PATCH] hud: wip scoreboard, fixes --- .../data/scoreboard/ScoreboardManager.kt | 17 ++- .../data/scoreboard/ScoreboardObjective.kt | 8 +- .../data/scoreboard/ScoreboardPositions.kt | 3 +- .../gui/rendering/gui/elements/Element.kt | 3 +- .../gui/elements/layout/RowLayout.kt | 2 +- .../gui/elements/text/TextFlowElement.kt | 2 +- .../gui/rendering/gui/hud/HUDRenderer.kt | 2 + .../scoreboard/ScoreboardHUDElement.kt | 69 ++++++++++ .../scoreboard/ScoreboardScoreElement.kt | 76 +++++++++++ .../scoreboard/ScoreboardSideElement.kt | 118 ++++++++++++++++++ .../gui/hud/elements/tab/TabListElement.kt | 4 +- .../hud/elements/tab/TabListEntryElement.kt | 4 +- .../events/scoreboard/ScoreTeamChangeEvent.kt | 15 +++ .../TeamCreateEvent.kt} | 4 +- .../TeamMemberAddEvent.kt} | 5 +- .../TeamMemberRemoveEvent.kt} | 5 +- .../events/scoreboard/team/TeamUpdateEvent.kt | 10 ++ .../network/connection/play/PlayConnection.kt | 2 +- .../RemoveScoreboardObjectiveS2CP.kt | 2 + .../score/RemoveScoreboardScoreS2CP.kt | 10 +- .../play/scoreboard/teams/TeamCreateS2CP.kt | 4 +- .../scoreboard/teams/TeamMemberAddS2CP.kt | 4 +- .../scoreboard/teams/TeamMemberRemoveS2CP.kt | 4 +- .../play/scoreboard/teams/TeamUpdateS2CP.kt | 23 ++-- .../minosoft/protocol/protocol/PacketTypes.kt | 6 +- .../java/de/bixilon/minosoft/util/KUtil.kt | 11 +- .../util/collections/SynchronizedMap.kt | 2 +- 27 files changed, 370 insertions(+), 45 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardHUDElement.kt create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardScoreElement.kt create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardSideElement.kt create mode 100644 src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/ScoreTeamChangeEvent.kt rename src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/{ScoreboardTeamCreateEvent.kt => team/TeamCreateEvent.kt} (76%) rename src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/{ScoreboardTeamMemberAddEvent.kt => team/TeamMemberAddEvent.kt} (59%) rename src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/{ScoreboardTeamMemberRemoveEvent.kt => team/TeamMemberRemoveEvent.kt} (58%) create mode 100644 src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/team/TeamUpdateEvent.kt diff --git a/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardManager.kt b/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardManager.kt index d7f28aefd..5b43d4a62 100644 --- a/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardManager.kt +++ b/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardManager.kt @@ -12,9 +12,12 @@ */ package de.bixilon.minosoft.data.scoreboard +import de.bixilon.minosoft.modding.event.events.scoreboard.ScoreTeamChangeEvent +import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.util.KUtil.synchronizedMapOf +import de.bixilon.minosoft.util.KUtil.toSynchronizedMap -class ScoreboardManager { +class ScoreboardManager(private val connection: PlayConnection) { val teams: MutableMap = synchronizedMapOf() val objectives: MutableMap = synchronizedMapOf() @@ -24,7 +27,7 @@ class ScoreboardManager { fun getTeamsOf(member: String): Set { val teams: MutableSet = mutableSetOf() - for ((name, team) in this.teams) { + for ((_, team) in this.teams) { if (!team.members.contains(member)) { continue } @@ -34,15 +37,19 @@ class ScoreboardManager { return teams } - fun updateScoreTeams(team: Team, members: Set, remove: Boolean = false) { - for ((_, objective) in objectives) { - for ((_, score) in objective.scores) { + fun updateScoreTeams(team: Team, members: Set, remove: Boolean = false, fireEvent: Boolean = true) { + for ((_, objective) in objectives.toSynchronizedMap()) { + for ((_, score) in objective.scores.toSynchronizedMap()) { if (score.entity in members) { if (remove) { score.teams -= team } else { score.teams += team } + if (!fireEvent) { + continue + } + connection.fireEvent(ScoreTeamChangeEvent(connection, objective, score, team, remove)) } } } diff --git a/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardObjective.kt b/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardObjective.kt index a2f657aa2..ec30f7587 100644 --- a/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardObjective.kt +++ b/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardObjective.kt @@ -14,11 +14,17 @@ package de.bixilon.minosoft.data.scoreboard import de.bixilon.minosoft.data.text.ChatComponent import de.bixilon.minosoft.protocol.packets.s2c.play.scoreboard.objective.CreateScoreboardObjectiveS2CP +import de.bixilon.minosoft.util.KUtil.synchronizedMapOf class ScoreboardObjective( val name: String, var displayName: ChatComponent, var unit: CreateScoreboardObjectiveS2CP.ObjectiveUnits, ) { - val scores: MutableMap = mutableMapOf() + val scores: MutableMap = synchronizedMapOf() + + + override fun toString(): String { + return name + } } diff --git a/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardPositions.kt b/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardPositions.kt index 62c5cc284..6c9bad240 100644 --- a/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardPositions.kt +++ b/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardPositions.kt @@ -4,9 +4,10 @@ import de.bixilon.minosoft.util.KUtil import de.bixilon.minosoft.util.enum.ValuesEnum enum class ScoreboardPositions { - LIST, + TAB_LIST, SIDEBAR, BELOW_NAME, + TEAM_BLACK, TEAM_DARK_BLUE, TEAM_DARK_GREEN, diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/Element.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/Element.kt index 0fb18a262..359ce3475 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/Element.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/Element.kt @@ -121,12 +121,13 @@ abstract class Element(val hudRenderer: HUDRenderer) { * @return The number of z layers used */ fun render(offset: Vec2i, z: Int, consumer: GUIVertexConsumer, options: GUIVertexOptions?): Int { + val offset = Vec2i(offset) if (RenderConstants.DISABLE_GUI_CACHE || !cacheEnabled) { return forceRender(offset, z, consumer, options) } if (!cacheUpToDate || cache.offset != offset || hudRenderer.matrixChange || cache.matrix !== hudRenderer.matrix || z != cache.z) { val cache = GUIMeshCache(hudRenderer.matrix) - cache.offset = offset + cache.offset = Vec2i(offset) cache.z = z val maxZ = forceRender(offset, z, cache, options) cache.maxZ = maxZ diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/layout/RowLayout.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/layout/RowLayout.kt index 1b487a110..b49f20a19 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/layout/RowLayout.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/layout/RowLayout.kt @@ -89,7 +89,7 @@ open class RowLayout( if (exceedsY(childSize.y)) { break } - val childZ = child.render(Vec2i(offset.x + margin.left + childAlignment.getOffset(size.x - margin.horizontal, childSize.x), offset.y + childYOffset), z, consumer, options) + val childZ = child.render(offset + Vec2i(margin.left + childAlignment.getOffset(size.x - margin.horizontal, childSize.x), childYOffset), z, consumer, options) if (maxZ < childZ) { maxZ = childZ } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextFlowElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextFlowElement.kt index 1930f0252..b96aa64ea 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextFlowElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextFlowElement.kt @@ -58,7 +58,7 @@ class TextFlowElement( yOffset += Font.TOTAL_CHAR_HEIGHT } - background.render(Vec2i(offset), z, consumer, options) + background.render(offset, z, consumer, options) return LAYERS } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/HUDRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/HUDRenderer.kt index 237106637..228ee285f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/HUDRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/HUDRenderer.kt @@ -34,6 +34,7 @@ import de.bixilon.minosoft.gui.rendering.gui.hud.elements.other.BreakProgressHUD import de.bixilon.minosoft.gui.rendering.gui.hud.elements.other.CrosshairHUDElement import de.bixilon.minosoft.gui.rendering.gui.hud.elements.other.DebugHUDElement import de.bixilon.minosoft.gui.rendering.gui.hud.elements.other.WorldInfoHUDElement +import de.bixilon.minosoft.gui.rendering.gui.hud.elements.scoreboard.ScoreboardHUDElement import de.bixilon.minosoft.gui.rendering.gui.hud.elements.tab.TabListHUDElement import de.bixilon.minosoft.gui.rendering.gui.hud.elements.title.TitleHUDElement import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIMesh @@ -104,6 +105,7 @@ class HUDRenderer( registerElement(HotbarHUDElement) registerElement(WorldInfoHUDElement) registerElement(TitleHUDElement) + registerElement(ScoreboardHUDElement) } override fun init() { 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 new file mode 100644 index 000000000..7c6d7cd08 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardHUDElement.kt @@ -0,0 +1,69 @@ +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.gui.hud.HUDRenderer +import de.bixilon.minosoft.gui.rendering.gui.hud.elements.HUDBuilder +import de.bixilon.minosoft.gui.rendering.gui.hud.elements.LayoutedHUDElement +import de.bixilon.minosoft.modding.event.events.scoreboard.* +import de.bixilon.minosoft.modding.event.events.scoreboard.team.TeamUpdateEvent +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) { + private val connection = hudRenderer.connection + override val layout = ScoreboardSideElement(hudRenderer) + + override val layoutOffset: Vec2i + get() = Vec2i(hudRenderer.scaledSize.x - layout.size.x, (hudRenderer.scaledSize.y - layout.size.y) / 2) + + override fun init() { + connection.registerEvent(CallbackEventInvoker.of { + if (it.position != ScoreboardPositions.SIDEBAR) { + return@of + } + + layout.objective = it.objective + }) + connection.registerEvent(CallbackEventInvoker.of { + if (it.objective != layout.objective) { + return@of + } + layout.updateName() + }) + connection.registerEvent(CallbackEventInvoker.of { + if (it.score.objective != layout.objective) { + return@of + } + layout.removeScore(it.score) + }) + connection.registerEvent(CallbackEventInvoker.of { + if (it.score.objective != layout.objective) { + return@of + } + layout.updateScore(it.score) + }) + connection.registerEvent(CallbackEventInvoker.of { + val objective = layout.objective ?: return@of + layout.updateScore(it.score) + }) + connection.registerEvent(CallbackEventInvoker.of { + val objective = layout.objective ?: return@of + for ((_, score) in objective.scores) { + if (it.team !in score.teams) { + continue + } + layout.updateScore(score) + } + }) + } + + companion object : HUDBuilder { + override val RESOURCE_LOCATION: ResourceLocation = "minosoft:scoreboard".toResourceLocation() + + override fun build(hudRenderer: HUDRenderer): ScoreboardHUDElement { + return ScoreboardHUDElement(hudRenderer) + } + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardScoreElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardScoreElement.kt new file mode 100644 index 000000000..01e157a5a --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardScoreElement.kt @@ -0,0 +1,76 @@ +package de.bixilon.minosoft.gui.rendering.gui.hud.elements.scoreboard + +import de.bixilon.minosoft.data.scoreboard.ScoreboardScore +import de.bixilon.minosoft.data.text.BaseComponent +import de.bixilon.minosoft.data.text.ChatColors +import de.bixilon.minosoft.data.text.TextComponent +import de.bixilon.minosoft.gui.rendering.gui.elements.Element +import de.bixilon.minosoft.gui.rendering.gui.elements.HorizontalAlignments +import de.bixilon.minosoft.gui.rendering.gui.elements.HorizontalAlignments.Companion.getOffset +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.gui.mesh.GUIVertexOptions +import glm_.vec2.Vec2i + +class ScoreboardScoreElement( + hudRenderer: HUDRenderer, + val score: ScoreboardScore, + parent: Element?, +) : Element(hudRenderer) { + private val nameElement = TextElement(hudRenderer, "", background = false, parent = this) + private val scoreElement = TextElement(hudRenderer, "", background = false, parent = this) + + init { + nameElement.prefMaxSize = Vec2i(-1, ScoreboardSideElement.SCORE_HEIGHT) + scoreElement.prefMaxSize = Vec2i(-1, ScoreboardSideElement.SCORE_HEIGHT) + forceSilentApply() + _parent = parent + } + + override fun forceRender(offset: Vec2i, z: Int, consumer: GUIVertexConsumer, options: GUIVertexOptions?): Int { + nameElement.render(offset, z, consumer, options) + + scoreElement.render(offset + Vec2i(HorizontalAlignments.RIGHT.getOffset(size.x, scoreElement.size.x), 0), z, consumer, options) + + return TextElement.LAYERS + } + + override fun silentApply(): Boolean { + forceSilentApply() + return true + } + + override fun forceSilentApply() { + val name = BaseComponent() + + // ToDo: Can a score (entity; whatever) can have multiple teams? + score.teams.iterator().apply { + if (!hasNext()) { + return@apply + } + val team = next() + name += team.prefix + name += team.suffix + } + name += score.entity + + nameElement.text = name + + scoreElement.text = TextComponent(score.value).color(ChatColors.RED) + + _prefSize = Vec2i(nameElement.size.x + scoreElement.size.x + SCORE_MIN_MARGIN, ScoreboardSideElement.SCORE_HEIGHT) + cacheUpToDate = false + } + + fun applySize() { + _size = parent?.size?.let { return@let Vec2i(it.x, ScoreboardSideElement.SCORE_HEIGHT) } ?: _prefSize + } + + override fun onChildChange(child: Element) = Unit + + + companion object { + private const val SCORE_MIN_MARGIN = 5 + } +} 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 new file mode 100644 index 000000000..56e8702bf --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/scoreboard/ScoreboardSideElement.kt @@ -0,0 +1,118 @@ +package de.bixilon.minosoft.gui.rendering.gui.hud.elements.scoreboard + +import de.bixilon.minosoft.data.scoreboard.ScoreboardObjective +import de.bixilon.minosoft.data.scoreboard.ScoreboardScore +import de.bixilon.minosoft.gui.rendering.RenderConstants +import de.bixilon.minosoft.gui.rendering.font.Font +import de.bixilon.minosoft.gui.rendering.gui.elements.Element +import de.bixilon.minosoft.gui.rendering.gui.elements.HorizontalAlignments +import de.bixilon.minosoft.gui.rendering.gui.elements.HorizontalAlignments.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.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.synchronizedMapOf +import de.bixilon.minosoft.util.KUtil.toSynchronizedMap +import glm_.vec2.Vec2i + +class ScoreboardSideElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { + private val backgroundElement = ColorElement(hudRenderer, size = Vec2i.EMPTY, color = RenderConstants.TEXT_BACKGROUND_COLOR) + private val nameBackgroundElement = ColorElement(hudRenderer, size = Vec2i.EMPTY, color = RenderConstants.TEXT_BACKGROUND_COLOR) + private val nameElement = TextElement(hudRenderer, "", background = false, parent = this) + private val scores: MutableMap = synchronizedMapOf() + + + var objective: ScoreboardObjective? = null + set(value) { + if (field == value) { + return + } + field = value + scores.clear() + forceSilentApply() + } + + 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) + + nameElement.render(offset + Vec2i(HorizontalAlignments.CENTER.getOffset(size.x, nameElement.size.x), 0), z + 2, consumer, options) + offset.y += Font.TOTAL_CHAR_HEIGHT + + val scores = scores.toSynchronizedMap().toSortedMap { a, b -> b.value - a.value } + for ((_, score) in scores) { + score.render(offset, z + 2, consumer, options) + offset.y += score.size.y + } + + return TextElement.LAYERS + 2 // 2 backgrounds + } + + override fun forceSilentApply() { + val objective = objective + if (objective == null) { + _size = Vec2i.EMPTY + return + } + + this.scores.clear() + + updateName() + + queueSizeRecalculation() + } + + private fun recalculateSize() { + val size = Vec2i(MIN_WIDTH, Font.TOTAL_CHAR_HEIGHT) + size.x = maxOf(size.x, nameElement.size.x) + + val scores = scores.toSynchronizedMap() + + + for ((_, element) in scores) { + element.forceSilentApply() + size.x = maxOf(size.x, element.prefSize.x) + } + + size.y += SCORE_HEIGHT * scores.size + + + + _size = size + nameBackgroundElement.size = Vec2i(size.x, SCORE_HEIGHT) + backgroundElement.size = size + + + for ((_, element) in scores) { + element.applySize() + } + } + + @Synchronized + private fun queueSizeRecalculation() { + cacheUpToDate = false + } + + fun removeScore(score: ScoreboardScore) { + scores.remove(score) ?: return + queueSizeRecalculation() + } + + fun updateScore(score: ScoreboardScore) { + scores.getOrPut(score) { ScoreboardScoreElement(hudRenderer, score, this) } + queueSizeRecalculation() + } + + fun updateName() { + nameElement.text = objective?.displayName ?: return + queueSizeRecalculation() + } + + companion object { + const val MIN_WIDTH = 30 + const val SCORE_HEIGHT = Font.TOTAL_CHAR_HEIGHT + } +} 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 420b85234..2ee654f13 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 @@ -51,7 +51,7 @@ class TabListElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { ) override fun forceRender(offset: Vec2i, z: Int, consumer: GUIVertexConsumer, options: GUIVertexOptions?): Int { - background.render(Vec2i(offset), z, consumer, options) + background.render(offset, z, consumer, options) offset += BACKGROUND_PADDING val size = size @@ -71,7 +71,7 @@ class TabListElement(hudRenderer: HUDRenderer) : Element(hudRenderer) { } for ((index, entry) in toRender.withIndex()) { - entry.render(Vec2i(offset), z + 1, consumer, options) + entry.render(offset, z + 1, consumer, options) offset.y += TabListEntryElement.HEIGHT + ENTRY_VERTICAL_SPACING if ((index + 1) % ENTRIES_PER_COLUMN == 0) { offset.x += entry.width + ENTRY_HORIZONTAL_SPACING 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 5063b0f0e..3acb5e6a3 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 @@ -72,8 +72,8 @@ class TabListEntryElement( } override fun forceRender(offset: Vec2i, z: Int, consumer: GUIVertexConsumer, options: GUIVertexOptions?): Int { - background.render(Vec2i(offset), z, consumer, options) - nameElement.render(Vec2i(offset), z, consumer, options) + background.render(offset, z, consumer, options) + nameElement.render(offset, z, consumer, options) pingElement.render(offset + Vec2i(HorizontalAlignments.RIGHT.getOffset(maxSize.x, pingElement.size.x + PADDING), PADDING), z + 1, consumer, options) return TextElement.LAYERS diff --git a/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/ScoreTeamChangeEvent.kt b/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/ScoreTeamChangeEvent.kt new file mode 100644 index 000000000..7803e28ee --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/ScoreTeamChangeEvent.kt @@ -0,0 +1,15 @@ +package de.bixilon.minosoft.modding.event.events.scoreboard + +import de.bixilon.minosoft.data.scoreboard.ScoreboardObjective +import de.bixilon.minosoft.data.scoreboard.ScoreboardScore +import de.bixilon.minosoft.data.scoreboard.Team +import de.bixilon.minosoft.modding.event.events.connection.play.PlayConnectionEvent +import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection + +class ScoreTeamChangeEvent( + connection: PlayConnection, + val objective: ScoreboardObjective, + val score: ScoreboardScore, + val team: Team, + val remove: Boolean, +) : PlayConnectionEvent(connection) diff --git a/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/ScoreboardTeamCreateEvent.kt b/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/team/TeamCreateEvent.kt similarity index 76% rename from src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/ScoreboardTeamCreateEvent.kt rename to src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/team/TeamCreateEvent.kt index 41ebca51a..5a7da8283 100644 --- a/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/ScoreboardTeamCreateEvent.kt +++ b/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/team/TeamCreateEvent.kt @@ -1,10 +1,10 @@ -package de.bixilon.minosoft.modding.event.events.scoreboard +package de.bixilon.minosoft.modding.event.events.scoreboard.team import de.bixilon.minosoft.data.scoreboard.Team import de.bixilon.minosoft.modding.event.events.connection.play.PlayConnectionEvent import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection -class ScoreboardTeamCreateEvent( +class TeamCreateEvent( connection: PlayConnection, val team: Team, ) : PlayConnectionEvent(connection) diff --git a/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/ScoreboardTeamMemberAddEvent.kt b/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/team/TeamMemberAddEvent.kt similarity index 59% rename from src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/ScoreboardTeamMemberAddEvent.kt rename to src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/team/TeamMemberAddEvent.kt index 2ebc33f88..beff1d089 100644 --- a/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/ScoreboardTeamMemberAddEvent.kt +++ b/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/team/TeamMemberAddEvent.kt @@ -1,9 +1,10 @@ -package de.bixilon.minosoft.modding.event.events.scoreboard +package de.bixilon.minosoft.modding.event.events.scoreboard.team import de.bixilon.minosoft.data.scoreboard.Team +import de.bixilon.minosoft.modding.event.events.scoreboard.ScoreboardTeamMemberEvent import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection -class ScoreboardTeamMemberAddEvent( +class TeamMemberAddEvent( connection: PlayConnection, team: Team, members: Set, diff --git a/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/ScoreboardTeamMemberRemoveEvent.kt b/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/team/TeamMemberRemoveEvent.kt similarity index 58% rename from src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/ScoreboardTeamMemberRemoveEvent.kt rename to src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/team/TeamMemberRemoveEvent.kt index c9c1e7cb0..df98f33ba 100644 --- a/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/ScoreboardTeamMemberRemoveEvent.kt +++ b/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/team/TeamMemberRemoveEvent.kt @@ -1,9 +1,10 @@ -package de.bixilon.minosoft.modding.event.events.scoreboard +package de.bixilon.minosoft.modding.event.events.scoreboard.team import de.bixilon.minosoft.data.scoreboard.Team +import de.bixilon.minosoft.modding.event.events.scoreboard.ScoreboardTeamMemberEvent import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection -class ScoreboardTeamMemberRemoveEvent( +class TeamMemberRemoveEvent( connection: PlayConnection, team: Team, members: Set, diff --git a/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/team/TeamUpdateEvent.kt b/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/team/TeamUpdateEvent.kt new file mode 100644 index 000000000..6e4a29279 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/modding/event/events/scoreboard/team/TeamUpdateEvent.kt @@ -0,0 +1,10 @@ +package de.bixilon.minosoft.modding.event.events.scoreboard.team + +import de.bixilon.minosoft.data.scoreboard.Team +import de.bixilon.minosoft.modding.event.events.connection.play.PlayConnectionEvent +import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection + +class TeamUpdateEvent( + connection: PlayConnection, + val team: Team, +) : PlayConnectionEvent(connection) diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/connection/play/PlayConnection.kt b/src/main/java/de/bixilon/minosoft/protocol/network/connection/play/PlayConnection.kt index c189a84d0..9fcaad66a 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/connection/play/PlayConnection.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/network/connection/play/PlayConnection.kt @@ -71,7 +71,7 @@ class PlayConnection( val recipes = Recipes() val world = World(this) val tabList = TabList() - val scoreboardManager = ScoreboardManager() + val scoreboardManager = ScoreboardManager(this) val bossbarManager = BossbarManager() @Deprecated(message = "PacketSender is deprecated") diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/objective/RemoveScoreboardObjectiveS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/objective/RemoveScoreboardObjectiveS2CP.kt index fad59d870..4628cb935 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/objective/RemoveScoreboardObjectiveS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/objective/RemoveScoreboardObjectiveS2CP.kt @@ -13,6 +13,7 @@ package de.bixilon.minosoft.protocol.packets.s2c.play.scoreboard.objective +import de.bixilon.minosoft.modding.event.events.scoreboard.ObjectivePositionSetEvent import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer @@ -35,6 +36,7 @@ class RemoveScoreboardObjectiveS2CP(val objective: String, buffer: PlayInByteBuf continue } connection.scoreboardManager.positions -= position + connection.fireEvent(ObjectivePositionSetEvent(connection, position, null)) } } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/score/RemoveScoreboardScoreS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/score/RemoveScoreboardScoreS2CP.kt index e6bdd0609..b7491d9f7 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/score/RemoveScoreboardScoreS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/score/RemoveScoreboardScoreS2CP.kt @@ -17,6 +17,7 @@ import de.bixilon.minosoft.modding.event.events.scoreboard.ScoreboardScoreRemove import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer +import de.bixilon.minosoft.util.KUtil.toSynchronizedMap import de.bixilon.minosoft.util.logging.Log import de.bixilon.minosoft.util.logging.LogLevels import de.bixilon.minosoft.util.logging.LogMessageType @@ -24,7 +25,14 @@ import de.bixilon.minosoft.util.logging.LogMessageType class RemoveScoreboardScoreS2CP(val entity: String, val objective: String?, buffer: PlayInByteBuffer) : PlayS2CPacket() { override fun handle(connection: PlayConnection) { - val objective = connection.scoreboardManager.objectives[objective] ?: return + val objective = connection.scoreboardManager.objectives[objective] ?: let { + for ((_, objective) in connection.scoreboardManager.objectives.toSynchronizedMap()) { + val score = objective.scores.remove(entity) ?: continue + + connection.fireEvent(ScoreboardScoreRemoveEvent(connection, score)) + } + return + } val score = objective.scores.remove(entity) ?: return connection.fireEvent(ScoreboardScoreRemoveEvent(connection, score)) diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamCreateS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamCreateS2CP.kt index 4003cac0b..f061fdb9e 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamCreateS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamCreateS2CP.kt @@ -19,7 +19,7 @@ import de.bixilon.minosoft.data.scoreboard.Team import de.bixilon.minosoft.data.scoreboard.TeamCollisionRules import de.bixilon.minosoft.data.text.ChatCode import de.bixilon.minosoft.data.text.ChatComponent -import de.bixilon.minosoft.modding.event.events.scoreboard.ScoreboardTeamCreateEvent +import de.bixilon.minosoft.modding.event.events.scoreboard.team.TeamCreateEvent import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer @@ -123,7 +123,7 @@ class TeamCreateS2CP(val name: String, buffer: PlayInByteBuffer) : PlayS2CPacket connection.scoreboardManager.updateScoreTeams(team, members) - connection.fireEvent(ScoreboardTeamCreateEvent(connection, team)) + connection.fireEvent(TeamCreateEvent(connection, team)) } override fun log() { diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamMemberAddS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamMemberAddS2CP.kt index 720da2e69..60d01819f 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamMemberAddS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamMemberAddS2CP.kt @@ -14,7 +14,7 @@ package de.bixilon.minosoft.protocol.packets.s2c.play.scoreboard.teams import de.bixilon.minosoft.Minosoft -import de.bixilon.minosoft.modding.event.events.scoreboard.ScoreboardTeamMemberAddEvent +import de.bixilon.minosoft.modding.event.events.scoreboard.team.TeamMemberAddEvent import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer @@ -41,7 +41,7 @@ class TeamMemberAddS2CP(val name: String, buffer: PlayInByteBuffer) : PlayS2CPac } connection.scoreboardManager.updateScoreTeams(team, members) - connection.fireEvent(ScoreboardTeamMemberAddEvent(connection, team, members)) + connection.fireEvent(TeamMemberAddEvent(connection, team, members)) } override fun log() { diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamMemberRemoveS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamMemberRemoveS2CP.kt index 18e89b665..0d7460695 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamMemberRemoveS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamMemberRemoveS2CP.kt @@ -13,7 +13,7 @@ package de.bixilon.minosoft.protocol.packets.s2c.play.scoreboard.teams -import de.bixilon.minosoft.modding.event.events.scoreboard.ScoreboardTeamMemberRemoveEvent +import de.bixilon.minosoft.modding.event.events.scoreboard.team.TeamMemberRemoveEvent import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer @@ -44,7 +44,7 @@ class TeamMemberRemoveS2CP(val name: String, buffer: PlayInByteBuffer) : PlayS2C } connection.scoreboardManager.updateScoreTeams(team, members, true) - connection.fireEvent(ScoreboardTeamMemberRemoveEvent(connection, team, members)) + connection.fireEvent(TeamMemberRemoveEvent(connection, team, members)) } override fun log() { diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamUpdateS2CP.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamUpdateS2CP.kt index 40d477f3e..0e612b4a4 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamUpdateS2CP.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/scoreboard/teams/TeamUpdateS2CP.kt @@ -18,6 +18,7 @@ import de.bixilon.minosoft.data.scoreboard.NameTagVisibilities import de.bixilon.minosoft.data.scoreboard.TeamCollisionRules import de.bixilon.minosoft.data.text.ChatCode import de.bixilon.minosoft.data.text.ChatComponent +import de.bixilon.minosoft.modding.event.events.scoreboard.team.TeamUpdateEvent import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection import de.bixilon.minosoft.protocol.packets.s2c.PlayS2CPacket import de.bixilon.minosoft.protocol.protocol.PlayInByteBuffer @@ -91,16 +92,18 @@ class TeamUpdateS2CP(val name: String, buffer: PlayInByteBuffer) : PlayS2CPacket override fun handle(connection: PlayConnection) { - connection.scoreboardManager.teams[name]?.let { - it.displayName = displayName - it.prefix = prefix - it.suffix = suffix - it.friendlyFire = friendlyFire - it.canSeeInvisibleTeam = canSeeInvisibleTeam - it.collisionRule = collisionRule - it.nameTagVisibility = nameTagVisibility - it.formattingCode = formattingCode - } + val team = connection.scoreboardManager.teams[name] ?: return + + team.displayName = displayName + team.prefix = prefix + team.suffix = suffix + team.friendlyFire = friendlyFire + team.canSeeInvisibleTeam = canSeeInvisibleTeam + team.collisionRule = collisionRule + team.nameTagVisibility = nameTagVisibility + team.formattingCode = formattingCode + + connection.fireEvent(TeamUpdateEvent(connection, team)) } override fun log() { diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketTypes.kt b/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketTypes.kt index 54b5ca9b0..a89e324cf 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketTypes.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketTypes.kt @@ -231,17 +231,17 @@ class PacketTypes { PLAY_CAMERA({ CameraS2CP(it) }), PLAY_HOTBAR_SLOT_SET({ HotbarSlotSetS2CP(it) }), PLAY_CHUNK_CENTER_SET({ ChunkCenterSetS2CP(it) }), - PLAY_OBJECTIVE_POSITION_SET({ ObjectivePositionSetS2CP(it) }), + PLAY_OBJECTIVE_POSITION_SET({ ObjectivePositionSetS2CP(it) }, isThreadSafe = false), PLAY_ENTITY_METADATA({ EntityMetadataS2CP(it) }, isThreadSafe = false), PLAY_ENTITY_ATTACH({ EntityAttachS2CP(it) }), PLAY_ENTITY_VELOCITY({ EntityVelocityS2CP(it) }), PLAY_ENTITY_EQUIPMENT({ EntityEquipmentS2CP(it) }), PLAY_EXPERIENCE_SET({ ExperienceSetS2CP(it) }), PLAY_HEALTH_SET({ HealthSetS2CP(it) }), - PLAY_SCOREBOARD_OBJECTIVE({ ScoreboardObjectiveS2CF.createPacket(it) }), + PLAY_SCOREBOARD_OBJECTIVE({ ScoreboardObjectiveS2CF.createPacket(it) }, isThreadSafe = false), PLAY_ENTITY_PASSENGER_SET({ EntityPassengerSetS2CP(it) }), PLAY_TEAMS({ TeamsS2CF.createPacket(it) }), - PLAY_UPDATE_SCORE({ ScoreboardScoreS2CF.createPacket(it) }), + PLAY_UPDATE_SCORE({ ScoreboardScoreS2CF.createPacket(it) }, isThreadSafe = false), PLAY_COMPASS_POSITION_SET({ CompassPositionSetS2CP(it) }), PLAY_WORLD_TIME_SET({ WorldTimeSetS2CP(it) }), PLAY_ENTITY_SOUND_EVENT({ EntitySoundEventS2CP(it) }), diff --git a/src/main/java/de/bixilon/minosoft/util/KUtil.kt b/src/main/java/de/bixilon/minosoft/util/KUtil.kt index 1d0c633ac..18f572b12 100644 --- a/src/main/java/de/bixilon/minosoft/util/KUtil.kt +++ b/src/main/java/de/bixilon/minosoft/util/KUtil.kt @@ -95,16 +95,21 @@ object KUtil { return Collections.synchronizedList(mutableListOf(*values)) } - private fun Any.synchronizedCopy(copier: () -> K): K { + private fun Any.synchronizedCopy(lock: Object? = null, copier: () -> K): K { val ret: K - synchronized(this) { + synchronized(lock ?: this) { ret = copier() } return ret } fun Map.toSynchronizedMap(): SynchronizedMap { - return synchronizedCopy { SynchronizedMap(this.toMutableMap()) } + val lock = if (this is SynchronizedMap<*, *>) { + this.lock + } else { + null + } + return synchronizedCopy(lock) { SynchronizedMap(this.toMutableMap()) } } fun Collection.toSynchronizedList(): MutableList { diff --git a/src/main/java/de/bixilon/minosoft/util/collections/SynchronizedMap.kt b/src/main/java/de/bixilon/minosoft/util/collections/SynchronizedMap.kt index 39012907b..664ca2918 100644 --- a/src/main/java/de/bixilon/minosoft/util/collections/SynchronizedMap.kt +++ b/src/main/java/de/bixilon/minosoft/util/collections/SynchronizedMap.kt @@ -22,7 +22,7 @@ import java.util.function.Function class SynchronizedMap( private val original: MutableMap, ) : MutableMap { - private val lock = Object() + internal val lock = Object() override val size: Int get() = synchronized(lock) { original.size }