From f10abc3d6981ab0a557b019ffb6b6e2e55c14f2d Mon Sep 17 00:00:00 2001 From: Bixilon Date: Thu, 16 Jun 2022 16:06:01 +0200 Subject: [PATCH] properly synchronized scoreboard, fix other scoreboard bugs --- .../data/scoreboard/ScoreboardManager.kt | 22 +++++++++++------ .../data/scoreboard/ScoreboardObjective.kt | 5 ++-- .../scoreboard/ScoreboardSideElement.kt | 24 +++++++++++++------ 3 files changed, 35 insertions(+), 16 deletions(-) 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 7dc1da9ec..e91dcbc75 100644 --- a/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardManager.kt +++ b/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardManager.kt @@ -1,6 +1,6 @@ /* * Minosoft - * Copyright (C) 2020 Moritz Zwerger + * Copyright (C) 2020-2022 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. * @@ -12,32 +12,38 @@ */ package de.bixilon.minosoft.data.scoreboard +import de.bixilon.kutil.collections.CollectionUtil.lockMapOf import de.bixilon.kutil.collections.CollectionUtil.synchronizedMapOf -import de.bixilon.kutil.collections.CollectionUtil.toSynchronizedMap +import de.bixilon.kutil.collections.map.LockMap import de.bixilon.kutil.primitive.BooleanUtil.decide import de.bixilon.minosoft.modding.event.events.scoreboard.ScoreTeamChangeEvent import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection class ScoreboardManager(private val connection: PlayConnection) { - val teams: MutableMap = synchronizedMapOf() - val objectives: MutableMap = synchronizedMapOf() + val teams: LockMap = lockMapOf() + val objectives: LockMap = lockMapOf() val positions: MutableMap = synchronizedMapOf() fun getTeam(member: String): Team? { - for ((_, team) in this.teams.toSynchronizedMap()) { + this.teams.lock.acquire() + for (team in this.teams.values) { if (member !in team.members) { continue } + this.teams.lock.release() return team } + this.teams.lock.release() return null } fun updateScoreTeams(team: Team, members: Set, remove: Boolean = false, fireEvent: Boolean = true) { - for ((_, objective) in objectives.toSynchronizedMap()) { - for ((_, score) in objective.scores.toSynchronizedMap()) { + objectives.lock.acquire() + for (objective in objectives.values) { + objective.scores.lock.acquire() + for (score in objective.scores.values) { if (score.entity in members) { score.team = remove.decide(null, team) if (!fireEvent) { @@ -46,6 +52,8 @@ class ScoreboardManager(private val connection: PlayConnection) { connection.fireEvent(ScoreTeamChangeEvent(connection, objective, score, team, remove)) } } + objective.scores.lock.release() } + objectives.lock.release() } } 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 bc4d64021..d867831cb 100644 --- a/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardObjective.kt +++ b/src/main/java/de/bixilon/minosoft/data/scoreboard/ScoreboardObjective.kt @@ -12,7 +12,8 @@ */ package de.bixilon.minosoft.data.scoreboard -import de.bixilon.kutil.collections.CollectionUtil.synchronizedMapOf +import de.bixilon.kutil.collections.CollectionUtil.lockMapOf +import de.bixilon.kutil.collections.map.LockMap import de.bixilon.minosoft.data.text.ChatComponent import de.bixilon.minosoft.protocol.packets.s2c.play.scoreboard.objective.CreateObjectiveS2CP @@ -21,7 +22,7 @@ class ScoreboardObjective( var displayName: ChatComponent, var unit: CreateObjectiveS2CP.ObjectiveUnits, ) { - val scores: MutableMap = synchronizedMapOf() + val scores: LockMap = lockMapOf() override fun toString(): String { 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 e984f027e..2bbf0d99e 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 @@ -14,8 +14,9 @@ package de.bixilon.minosoft.gui.rendering.gui.hud.elements.scoreboard import de.bixilon.kotlinglm.vec2.Vec2i -import de.bixilon.kutil.collections.CollectionUtil.synchronizedMapOf +import de.bixilon.kutil.collections.CollectionUtil.lockMapOf import de.bixilon.kutil.collections.CollectionUtil.toSynchronizedMap +import de.bixilon.kutil.collections.map.LockMap import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.scoreboard.ScoreboardObjective import de.bixilon.minosoft.data.scoreboard.ScoreboardPositions @@ -45,7 +46,7 @@ class ScoreboardSideElement(guiRenderer: GUIRenderer) : Element(guiRenderer), La private val backgroundElement = ColorElement(guiRenderer, size = Vec2i.EMPTY, color = RenderConstants.TEXT_BACKGROUND_COLOR) private val nameBackgroundElement = ColorElement(guiRenderer, size = Vec2i.EMPTY, color = RenderConstants.TEXT_BACKGROUND_COLOR) private val nameElement = TextElement(guiRenderer, "", background = false, parent = this) - private val scores: MutableMap = synchronizedMapOf() + private val scores: LockMap = lockMapOf() override val layoutOffset: Vec2i get() = super.size.let { return@let Vec2i(guiRenderer.scaledSize.x - it.x, (guiRenderer.scaledSize.y - it.y) / 2) } @@ -74,11 +75,14 @@ class ScoreboardSideElement(guiRenderer: GUIRenderer) : Element(guiRenderer), La nameElement.render(offset + Vec2i(HorizontalAlignments.CENTER.getOffset(size.x, nameElement.size.x), 0), consumer, options) offset.y += Font.TOTAL_CHAR_HEIGHT - val scores = scores.toSynchronizedMap().entries.sortedWith { a, b -> a.key.compareTo(b.key) } + this.scores.lock.acquire() + val scores = this.scores.unsafe.entries.sortedWith { a, b -> a.key.compareTo(b.key) } + this.scores.lock.release() + var index = 0 for ((_, score) in scores) { score.render(offset, consumer, options) - offset.y += score.size.y + offset.y += Font.TOTAL_CHAR_HEIGHT if (++index >= MAX_SCORES) { break @@ -93,7 +97,14 @@ class ScoreboardSideElement(guiRenderer: GUIRenderer) : Element(guiRenderer), La return } - this.scores.clear() + this.scores.lock.lock() + this.scores.unsafe.clear() + objective.scores.lock.acquire() + for (score in objective.scores.values) { + this.scores.unsafe.getOrPut(score) { ScoreboardScoreElement(guiRenderer, score, this) } + } + objective.scores.lock.release() + this.scores.lock.unlock() updateName() @@ -131,7 +142,6 @@ class ScoreboardSideElement(guiRenderer: GUIRenderer) : Element(guiRenderer), La } } - @Synchronized private fun queueSizeRecalculation() { cacheUpToDate = false } @@ -142,7 +152,7 @@ class ScoreboardSideElement(guiRenderer: GUIRenderer) : Element(guiRenderer), La } fun updateScore(score: ScoreboardScore) { - scores.getOrPut(score) { ScoreboardScoreElement(guiRenderer, score, this) } + scores.synchronizedGetOrPut(score) { ScoreboardScoreElement(guiRenderer, score, this) } queueSizeRecalculation() }