From d3cad4eab48d4f9a4637f8a6be64f88b1d9972c4 Mon Sep 17 00:00:00 2001 From: Cam Walter Date: Tue, 26 Mar 2024 16:51:48 -0500 Subject: [PATCH] Scoreboard: Completely revamp the API Adds mutability to Scoreboard.Score among these other changes: (added unless otherwise stated) Team: - getColor - setColor Scoreboard: - addLine - createTeam - removeIndex - removeScores Scoreboard.Score: - getName changed to return String. made to be more consistent with other CT Apis, though could change those instead to return TextComponents - getNumberFormat - getTeam - remove - reset - resetName - resetNumberFormat - resetPoints - resetTeam - setName - setNumberFormat - setPoints - setTeam --- api/ctjs.api | 36 ++- .../internal/mixins/MinecraftClientMixin.java | 6 +- .../internal/mixins/Scoreboard$1Accessor.java | 15 + .../mixins/ScoreboardObjectiveMixin.java | 19 ++ src/main/kotlin/com/chattriggers/ctjs/CTJS.kt | 2 + .../com/chattriggers/ctjs/api/entity/Team.kt | 21 ++ .../ctjs/api/message/TextComponent.kt | 4 +- .../chattriggers/ctjs/api/world/Scoreboard.kt | 293 ++++++++++++++++-- .../internal/console/ConsoleHostProcess.kt | 2 +- .../ctjs/internal/utils/ResettableState.kt | 10 + src/main/resources/chattriggers.mixins.json | 38 ++- 11 files changed, 400 insertions(+), 46 deletions(-) create mode 100644 src/main/java/com/chattriggers/ctjs/internal/mixins/Scoreboard$1Accessor.java create mode 100644 src/main/java/com/chattriggers/ctjs/internal/mixins/ScoreboardObjectiveMixin.java create mode 100644 src/main/kotlin/com/chattriggers/ctjs/internal/utils/ResettableState.kt diff --git a/api/ctjs.api b/api/ctjs.api index 295bbf0e..e602d465 100644 --- a/api/ctjs.api +++ b/api/ctjs.api @@ -1013,6 +1013,7 @@ public final class com/chattriggers/ctjs/api/entity/PlayerMP : com/chattriggers/ public final class com/chattriggers/ctjs/api/entity/Team : com/chattriggers/ctjs/api/CTWrapper { public fun (Lnet/minecraft/scoreboard/Team;)V public final fun canSeeInvisibleTeammates ()Z + public final fun getColor ()Ljava/lang/String; public final fun getDeathMessageVisibility ()Lcom/chattriggers/ctjs/api/entity/Team$Visibility; public final fun getFriendlyFire ()Z public synthetic fun getMcValue ()Ljava/lang/Object; @@ -1023,6 +1024,7 @@ public final class com/chattriggers/ctjs/api/entity/Team : com/chattriggers/ctjs public final fun getPrefix ()Ljava/lang/String; public final fun getRegisteredName ()Ljava/lang/String; public final fun getSuffix ()Ljava/lang/String; + public final fun setColor (Ljava/lang/Object;)Lcom/chattriggers/ctjs/api/entity/Team; public final fun setName (Lcom/chattriggers/ctjs/api/message/TextComponent;)Lcom/chattriggers/ctjs/api/entity/Team; public final fun setName (Ljava/lang/String;)Lcom/chattriggers/ctjs/api/entity/Team; public final fun setPrefix (Lcom/chattriggers/ctjs/api/message/TextComponent;)Lcom/chattriggers/ctjs/api/entity/Team; @@ -1033,6 +1035,10 @@ public final class com/chattriggers/ctjs/api/entity/Team : com/chattriggers/ctjs public fun toMC ()Lnet/minecraft/scoreboard/Team; } +public synthetic class com/chattriggers/ctjs/api/entity/Team$EntriesMappings { + public static final synthetic field entries$0 Lkotlin/enums/EnumEntries; +} + public final class com/chattriggers/ctjs/api/entity/Team$Visibility : java/lang/Enum, com/chattriggers/ctjs/api/CTWrapper { public static final field ALWAYS Lcom/chattriggers/ctjs/api/entity/Team$Visibility; public static final field Companion Lcom/chattriggers/ctjs/api/entity/Team$Visibility$Companion; @@ -2314,6 +2320,9 @@ public final class com/chattriggers/ctjs/api/world/PotionEffectType { public final class com/chattriggers/ctjs/api/world/Scoreboard { public static final field INSTANCE Lcom/chattriggers/ctjs/api/world/Scoreboard; + public static final fun addLine (ILcom/chattriggers/ctjs/api/message/TextComponent;)V + public static final fun addLine (ILjava/lang/String;)V + public static final fun createTeam (Ljava/lang/String;)Lcom/chattriggers/ctjs/api/entity/Team; public static final fun getLineByIndex (I)Lcom/chattriggers/ctjs/api/world/Scoreboard$Score; public static final fun getLines ()Ljava/util/List; public static final fun getLines (Z)Ljava/util/List; @@ -2323,6 +2332,10 @@ public final class com/chattriggers/ctjs/api/world/Scoreboard { public static final fun getShouldRender ()Z public static final fun getSidebar ()Lnet/minecraft/scoreboard/ScoreboardObjective; public static final fun getTitle ()Lcom/chattriggers/ctjs/api/message/TextComponent; + public static final fun removeIndex (I)V + public static final fun removeIndex (IZ)V + public static synthetic fun removeIndex$default (IZILjava/lang/Object;)V + public static final fun removeScores (I)V public static final fun setLine (ILcom/chattriggers/ctjs/api/message/TextComponent;)V public static final fun setLine (ILcom/chattriggers/ctjs/api/message/TextComponent;Z)V public static final fun setLine (ILjava/lang/String;)V @@ -2335,11 +2348,26 @@ public final class com/chattriggers/ctjs/api/world/Scoreboard { public static final fun toMC ()Lnet/minecraft/scoreboard/Scoreboard; } -public final class com/chattriggers/ctjs/api/world/Scoreboard$Score { - public fun (Lnet/minecraft/scoreboard/ScoreboardEntry;)V - public final fun getName ()Lcom/chattriggers/ctjs/api/message/TextComponent; +public final class com/chattriggers/ctjs/api/world/Scoreboard$Score : com/chattriggers/ctjs/api/CTWrapper { + public fun (Lnet/minecraft/scoreboard/ScoreAccess;)V + public synthetic fun getMcValue ()Ljava/lang/Object; + public fun getMcValue ()Lnet/minecraft/scoreboard/ScoreAccess; + public final fun getName ()Ljava/lang/String; + public final fun getNumberFormat ()Lnet/minecraft/scoreboard/number/NumberFormat; public final fun getPoints ()I - public final fun toMC ()Lnet/minecraft/scoreboard/ScoreboardEntry; + public final fun getTeam ()Lcom/chattriggers/ctjs/api/entity/Team; + public final fun remove ()V + public final fun reset ()Lcom/chattriggers/ctjs/api/world/Scoreboard$Score; + public final fun resetName ()Lcom/chattriggers/ctjs/api/world/Scoreboard$Score; + public final fun resetNumberFormat ()Lcom/chattriggers/ctjs/api/world/Scoreboard$Score; + public final fun resetPoints ()Lcom/chattriggers/ctjs/api/world/Scoreboard$Score; + public final fun resetTeam ()Lcom/chattriggers/ctjs/api/world/Scoreboard$Score; + public final fun setName (Lcom/chattriggers/ctjs/api/message/TextComponent;)Lcom/chattriggers/ctjs/api/world/Scoreboard$Score; + public final fun setNumberFormat (Ljava/lang/Object;)Lcom/chattriggers/ctjs/api/world/Scoreboard$Score; + public final fun setPoints (I)Lcom/chattriggers/ctjs/api/world/Scoreboard$Score; + public final fun setTeam (Lcom/chattriggers/ctjs/api/entity/Team;)Lcom/chattriggers/ctjs/api/world/Scoreboard$Score; + public synthetic fun toMC ()Ljava/lang/Object; + public fun toMC ()Lnet/minecraft/scoreboard/ScoreAccess; public fun toString ()Ljava/lang/String; } diff --git a/src/main/java/com/chattriggers/ctjs/internal/mixins/MinecraftClientMixin.java b/src/main/java/com/chattriggers/ctjs/internal/mixins/MinecraftClientMixin.java index 7ed706f6..c42ccf3b 100644 --- a/src/main/java/com/chattriggers/ctjs/internal/mixins/MinecraftClientMixin.java +++ b/src/main/java/com/chattriggers/ctjs/internal/mixins/MinecraftClientMixin.java @@ -1,5 +1,6 @@ package com.chattriggers.ctjs.internal.mixins; +import com.chattriggers.ctjs.api.world.Scoreboard; import com.chattriggers.ctjs.internal.engine.CTEvents; import com.chattriggers.ctjs.api.triggers.TriggerType; import com.chattriggers.ctjs.internal.engine.module.ModuleManager; @@ -29,8 +30,10 @@ private void injectWorldUnload(ClientWorld world, CallbackInfo ci) { TriggerType.SERVER_DISCONNECT.triggerAll(); } - if (this.world != null) + if (this.world != null) { TriggerType.WORLD_UNLOAD.triggerAll(); + Scoreboard.INSTANCE.clearCustom$ctjs(); + } } @Inject(method = "joinWorld", at = @At("TAIL")) @@ -46,6 +49,7 @@ private void injectDisconnect(Screen screen, CallbackInfo ci) { if (this.isIntegratedServerRunning() || this.getCurrentServerEntry() != null) { TriggerType.WORLD_UNLOAD.triggerAll(); TriggerType.SERVER_DISCONNECT.triggerAll(); + Scoreboard.INSTANCE.clearCustom$ctjs(); } } diff --git a/src/main/java/com/chattriggers/ctjs/internal/mixins/Scoreboard$1Accessor.java b/src/main/java/com/chattriggers/ctjs/internal/mixins/Scoreboard$1Accessor.java new file mode 100644 index 00000000..3c9124bd --- /dev/null +++ b/src/main/java/com/chattriggers/ctjs/internal/mixins/Scoreboard$1Accessor.java @@ -0,0 +1,15 @@ +package com.chattriggers.ctjs.internal.mixins; + +import net.minecraft.scoreboard.ScoreHolder; +import net.minecraft.scoreboard.ScoreboardScore; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(targets = "net.minecraft.scoreboard.Scoreboard$1") +public interface Scoreboard$1Accessor { + @Accessor("field_47543") + ScoreboardScore getScore(); + + @Accessor("field_47547") + ScoreHolder getHolder(); +} diff --git a/src/main/java/com/chattriggers/ctjs/internal/mixins/ScoreboardObjectiveMixin.java b/src/main/java/com/chattriggers/ctjs/internal/mixins/ScoreboardObjectiveMixin.java new file mode 100644 index 00000000..f8b2cffa --- /dev/null +++ b/src/main/java/com/chattriggers/ctjs/internal/mixins/ScoreboardObjectiveMixin.java @@ -0,0 +1,19 @@ +package com.chattriggers.ctjs.internal.mixins; + +import com.chattriggers.ctjs.api.world.Scoreboard; +import net.minecraft.scoreboard.ScoreboardObjective; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ScoreboardObjective.class) +public class ScoreboardObjectiveMixin { + @Inject(method = "setDisplayName", at = @At("HEAD"), cancellable = true) + private void chattriggers$keepCustomName(Text name, CallbackInfo ci) { + if (Scoreboard.INSTANCE.getCustomTitle$ctjs()) { + ci.cancel(); + } + } +} diff --git a/src/main/kotlin/com/chattriggers/ctjs/CTJS.kt b/src/main/kotlin/com/chattriggers/ctjs/CTJS.kt index 47a3d14d..7513994d 100644 --- a/src/main/kotlin/com/chattriggers/ctjs/CTJS.kt +++ b/src/main/kotlin/com/chattriggers/ctjs/CTJS.kt @@ -9,6 +9,7 @@ import com.chattriggers.ctjs.api.commands.DynamicCommands import com.chattriggers.ctjs.api.message.ChatLib import com.chattriggers.ctjs.api.render.Image import com.chattriggers.ctjs.api.triggers.TriggerType +import com.chattriggers.ctjs.api.world.Scoreboard import com.chattriggers.ctjs.api.world.World import com.chattriggers.ctjs.engine.Console import com.chattriggers.ctjs.engine.Register @@ -85,6 +86,7 @@ class CTJS : ClientModInitializer { fun unload(asCommand: Boolean = true) { TriggerType.WORLD_UNLOAD.triggerAll() TriggerType.GAME_UNLOAD.triggerAll() + Scoreboard.clearCustom() isLoaded = false diff --git a/src/main/kotlin/com/chattriggers/ctjs/api/entity/Team.kt b/src/main/kotlin/com/chattriggers/ctjs/api/entity/Team.kt index 54f817c1..76f04cd7 100644 --- a/src/main/kotlin/com/chattriggers/ctjs/api/entity/Team.kt +++ b/src/main/kotlin/com/chattriggers/ctjs/api/entity/Team.kt @@ -3,7 +3,10 @@ package com.chattriggers.ctjs.api.entity import com.chattriggers.ctjs.api.CTWrapper import com.chattriggers.ctjs.api.message.TextComponent import com.chattriggers.ctjs.MCTeam +import com.chattriggers.ctjs.api.message.ChatLib import net.minecraft.scoreboard.AbstractTeam +import net.minecraft.text.TextColor +import net.minecraft.util.Formatting class Team(override val mcValue: MCTeam) : CTWrapper { /** @@ -79,6 +82,24 @@ class Team(override val mcValue: MCTeam) : CTWrapper { */ fun setSuffix(suffix: String) = setSuffix(TextComponent(suffix)) + fun getColor() = mcValue.color.toString() + + /** + * Sets the team color + * @param color a string format of a [Formatting], or a hex value + * @return the team for method chaining + */ + fun setColor(color: Any?) = apply { + mcValue.color = when (color) { + is Number -> Formatting.byColorIndex(color.toInt()) + is CharSequence -> Formatting.entries.find { + it.toString() == ChatLib.addColor(color.toString()) + } ?: Formatting.RESET + null -> Formatting.RESET + else -> throw IllegalArgumentException("Could not convert type ${color::class.simpleName} to a Formatting") + } + } + /** * Gets the team's friendly fire setting */ diff --git a/src/main/kotlin/com/chattriggers/ctjs/api/message/TextComponent.kt b/src/main/kotlin/com/chattriggers/ctjs/api/message/TextComponent.kt index fb26d6ed..48e746ea 100644 --- a/src/main/kotlin/com/chattriggers/ctjs/api/message/TextComponent.kt +++ b/src/main/kotlin/com/chattriggers/ctjs/api/message/TextComponent.kt @@ -399,12 +399,12 @@ class TextComponent private constructor( } } - private companion object { + internal companion object { private val colorToFormatChar = Formatting.entries.mapNotNull { format -> TextColor.fromFormatting(format)?.let { it to format } }.toMap() - private fun jsObjectToStyle(obj: NativeObject): Style { + internal fun jsObjectToStyle(obj: NativeObject): Style { return Style.EMPTY .withColor(obj["color"]?.let { color -> when (color) { diff --git a/src/main/kotlin/com/chattriggers/ctjs/api/world/Scoreboard.kt b/src/main/kotlin/com/chattriggers/ctjs/api/world/Scoreboard.kt index 9d718de7..ff970c92 100644 --- a/src/main/kotlin/com/chattriggers/ctjs/api/world/Scoreboard.kt +++ b/src/main/kotlin/com/chattriggers/ctjs/api/world/Scoreboard.kt @@ -1,16 +1,28 @@ package com.chattriggers.ctjs.api.world -import com.chattriggers.ctjs.api.message.TextComponent import com.chattriggers.ctjs.MCTeam -import net.minecraft.scoreboard.ScoreboardEntry +import com.chattriggers.ctjs.api.CTWrapper +import com.chattriggers.ctjs.api.entity.Team +import com.chattriggers.ctjs.api.message.TextComponent +import com.chattriggers.ctjs.internal.mixins.`Scoreboard$1Accessor` +import com.chattriggers.ctjs.internal.utils.ResettableState +import com.chattriggers.ctjs.internal.utils.asMixin +import net.minecraft.scoreboard.ScoreAccess +import net.minecraft.scoreboard.ScoreboardDisplaySlot import net.minecraft.scoreboard.ScoreboardObjective import net.minecraft.scoreboard.ScoreboardScore +import net.minecraft.scoreboard.number.NumberFormat +import net.minecraft.scoreboard.number.StyledNumberFormat +import net.minecraft.text.Style +import org.mozilla.javascript.NativeObject object Scoreboard { private var needsUpdate = true private var scoreboardNames = mutableListOf() private var scoreboardTitle = TextComponent("") private var shouldRender = true + internal var customTitle = false + private val customLines = mutableMapOf() @JvmStatic fun toMC() = World.toMC()?.scoreboard @@ -20,7 +32,7 @@ object Scoreboard { fun getScoreboard() = toMC() @JvmStatic - fun getSidebar(): ScoreboardObjective? = toMC()?.objectives?.firstOrNull() + fun getSidebar(): ScoreboardObjective? = toMC()?.getObjectiveForSlot(ScoreboardDisplaySlot.SIDEBAR) /** * Gets the top-most string which is displayed on the scoreboard. (doesn't have a score on the side). @@ -46,7 +58,10 @@ object Scoreboard { */ @JvmStatic fun setTitle(title: TextComponent) { + customTitle = false getSidebar()?.displayName = title + scoreboardTitle = title + customTitle = true } @JvmStatic @@ -96,7 +111,7 @@ object Scoreboard { * Sets a line in the scoreboard to the specified name and score. * * @param score the score value for this item - * @param line the string to display on said line + * @param line the [TextComponent] to display on said line * @param override whether to remove old lines with the same score */ @JvmStatic @@ -115,8 +130,10 @@ object Scoreboard { scoreboard.knownScoreHolders.forEach { val scoreboardScore = scoreboard.getScore({ it.nameForScoreboard }, sidebarObjective) as? ScoreboardScore - if (scoreboardScore?.score == score) + if (scoreboardScore?.score == score) { scoreboardScore.displayText = line + customLines[score] = scoreboardScore + } } } @@ -124,6 +141,69 @@ object Scoreboard { @JvmOverloads fun setLine(score: Int, line: String, override: Boolean = false) = setLine(score, TextComponent(line), override) + /** + * Adds a line to the scoreboard + * + * @param score the score value for this item + * @param line the [TextComponent] to display on said line + */ + @JvmStatic + fun addLine(score: Int, line: TextComponent) { + val scoreboard = toMC() ?: return + val sidebarObjective = getSidebar() ?: return + + val newLine = scoreboard.getOrCreateScore({ line.formattedText }, sidebarObjective, true) + newLine.score = score + + customLines[score] = scoreboard.getScore({ line.formattedText }, sidebarObjective) as ScoreboardScore + + updateNames() + } + + @JvmStatic + fun addLine(score: Int, line: String) = addLine(score, TextComponent(line)) + + /** + * Removes all lines from the scoreboard matching with a certain score + * + * @param score the score of the lines to remove + */ + @JvmStatic + fun removeScores(score: Int) { + val scoreboard = toMC() ?: return + val sidebarObjective = getSidebar() ?: return + + scoreboard.knownScoreHolders.forEach { + val scoreboardScore = scoreboard.getScore({ it.nameForScoreboard }, sidebarObjective) ?: return@forEach + if (scoreboardScore.score == score) { + scoreboard.removeScore(it, sidebarObjective) + } + + if (customLines[score] == scoreboardScore) { + customLines.remove(score) + } + } + updateNames() + } + + /** + * Removes the line at a certain index + * + * @param index the index of the line to remove + */ + @JvmStatic + @JvmOverloads + fun removeIndex(index: Int, descending: Boolean = true) { + val scoreboard = toMC() ?: return + val sidebarObjective = getSidebar() ?: return + + val names = if (descending) scoreboardNames else scoreboardNames.asReversed() + val line = names.removeAt(index) + + scoreboard.removeScore(line.toMC().asMixin<`Scoreboard$1Accessor`>().holder, sidebarObjective) + updateNames() + } + @JvmStatic fun setShouldRender(shouldRender: Boolean) { Scoreboard.shouldRender = shouldRender @@ -132,25 +212,92 @@ object Scoreboard { @JvmStatic fun getShouldRender() = shouldRender + /** + * Creates or gets a [Team] with a given name + * + * @param name the name of the team + */ + @JvmStatic + fun createTeam(name: String): Team = Team(toMC()!!.addTeam(name)) + private fun updateNames() { - scoreboardNames.clear() - scoreboardTitle = TextComponent("") + scoreboardNames = scoreboardNames.filter { it.isCustom || it.getPoints() in customLines }.toMutableList() + + if (!customTitle) + scoreboardTitle = TextComponent("") val scoreboard = toMC() ?: return - val objective = scoreboard.objectives.singleOrNull() ?: return + val objective = getSidebar() ?: return - scoreboardTitle = TextComponent(objective.displayName) - scoreboardNames = scoreboard.getScoreboardEntries(objective).filter { - it.owner != null && !it.owner.startsWith("#") - }.map(::Score).sortedBy { it.getPoints() }.toMutableList() + if (!customTitle) + scoreboardTitle = TextComponent(objective.displayName) + + val newScores = scoreboard.knownScoreHolders.asSequence().filter { + objective in scoreboard.getScoreHolderObjectives(it) + }.map { + scoreboard.getOrCreateScore(it, objective, true) + }.filter { + it.score !in customLines + }.mapTo(mutableListOf(), ::Score) + + scoreboardNames = (scoreboardNames + newScores).sortedBy { it.getPoints() }.toMutableList() } internal fun resetCache() { needsUpdate = true } - class Score(private val mcValue: ScoreboardEntry) { - fun toMC() = mcValue + internal fun clearCustom() { + customLines.clear() + scoreboardNames.clear() + customTitle = false + scoreboardTitle = TextComponent("") + } + + class Score(override val mcValue: ScoreAccess) : CTWrapper { + private val pointsState = ResettableState(mcValue.score) + private val nameState = ResettableState(mcValue.displayText) + private val formatState = + ResettableState(mcValue.asMixin<`Scoreboard$1Accessor`>().score.numberFormat) + private val teamState = ResettableState(getTeam()) + + internal val isCustom: Boolean + get() = !nameState.isOriginalValue || !pointsState.isOriginalValue || !formatState.isOriginalValue || !teamState.isOriginalValue + + /** + * Gets the team associated with this score, if it exists + * + * @return the team, or null if it does not exist + */ + fun getTeam(): Team? = teamState.get() + + /** + * Sets the team associated with this score + * + * @param team the new team to set for this line. Custom teams can be created using [createTeam] + * @return the score to allow for method chaining + */ + fun setTeam(team: Team?) = apply { + val scoreboard = Scoreboard.toMC()!! + val name = mcValue.asMixin<`Scoreboard$1Accessor`>().holder.nameForScoreboard + + if (team == null) { + scoreboard.clearTeam(name) + } else { + scoreboard.addScoreHolderToTeam(name, team.toMC()) + } + + teamState.set(team) + } + + /** + * Resets the team of this score to its original state + * + * @return the team allow for method chaining + */ + fun resetTeam() = apply { + teamState.reset() + } /** * Gets the score point value for this score, @@ -158,20 +305,124 @@ object Scoreboard { * * @return the actual point value */ - fun getPoints(): Int = mcValue.value + fun getPoints(): Int = pointsState.get() + + /** + * Sets the point value for this score + * + * @param points the new point value + * @return the score to allow for method chaining + */ + fun setPoints(points: Int) = apply { + pointsState.set(points) + mcValue.score = points + } + + /** + * Resets the points of this score to its original state + * + * @return the team to allow for method chaining + */ + fun resetPoints() = apply { + pointsState.reset() + mcValue.score = pointsState.get() + } /** * Gets the display string of this score * * @return the display name */ - fun getName() = TextComponent( - MCTeam.decorateName( - Scoreboard.toMC()!!.getScoreHolderTeam(mcValue.owner), - TextComponent(mcValue.owner), + fun getName(): String { + val name = mcValue.asMixin<`Scoreboard$1Accessor`>().holder.nameForScoreboard + + val component = TextComponent( + MCTeam.decorateName( + getTeam()?.mcValue, + TextComponent(nameState.get() ?: name), + ) ) - ) - override fun toString(): String = getName().formattedText + return component.formattedText + } + + /** + * Sets the name of this score + * + * @param name the new name + * @return the score to allow for method chaining + */ + fun setName(name: TextComponent?) = apply { + nameState.set(name) + mcValue.displayText = name + } + + /** + * Resets the name of this score to its original state + */ + fun resetName() = apply { + nameState.reset() + mcValue.displayText = nameState.get() + } + + /** + * Gets the number format of this score + * + * @return the number format + */ + fun getNumberFormat(): NumberFormat? = formatState.get() + + /** + * Sets the number format of this score + * + * @param format either a formatting string, style in the form of an object, [NumberFormat], or hex value + * @return the score to allow for method chaining + * + * @see [TextComponent] + */ + fun setNumberFormat(format: Any?) = apply { + val style = when (format) { + is CharSequence -> StyledNumberFormat(TextComponent(format.toString()).style) + is NativeObject -> StyledNumberFormat(TextComponent.jsObjectToStyle(format)) + is NumberFormat -> format + is Number -> StyledNumberFormat(Style.EMPTY.withColor(format.toInt())) + else -> null + } + + formatState.set(style) + mcValue.setNumberFormat(style) + } + + /** + * Resets the number format of this score to its original state + */ + fun resetNumberFormat() = apply { + formatState.reset() + mcValue.setNumberFormat(formatState.get()) + } + + /** + * Resets the name, points, number format, and team of this score to their original states + */ + fun reset() = apply { + resetName() + resetPoints() + resetNumberFormat() + resetTeam() + } + + /** + * Removes this score from the scoreboard + */ + fun remove() { + val scoreboard = Scoreboard.toMC() ?: return + val sidebarObjective = getSidebar() ?: return + + scoreboard.removeScore(toMC().asMixin<`Scoreboard$1Accessor`>().holder, sidebarObjective) + scoreboardNames.remove(this) + updateNames() + } + + override fun toString(): String = getName() } } diff --git a/src/main/kotlin/com/chattriggers/ctjs/internal/console/ConsoleHostProcess.kt b/src/main/kotlin/com/chattriggers/ctjs/internal/console/ConsoleHostProcess.kt index 479db26f..9954d38c 100644 --- a/src/main/kotlin/com/chattriggers/ctjs/internal/console/ConsoleHostProcess.kt +++ b/src/main/kotlin/com/chattriggers/ctjs/internal/console/ConsoleHostProcess.kt @@ -130,7 +130,7 @@ object ConsoleHostProcess : Initializer { when (val message = Json.decodeFromString(messageText)) { is EvalTextMessage -> { - val result = JSLoader.eval(message.string) + val result = JSLoader.eval(message.string) ?: continue trySendMessage(EvalResultMessage(message.id, result)) } is FontSizeMessage -> { diff --git a/src/main/kotlin/com/chattriggers/ctjs/internal/utils/ResettableState.kt b/src/main/kotlin/com/chattriggers/ctjs/internal/utils/ResettableState.kt new file mode 100644 index 00000000..981e01d8 --- /dev/null +++ b/src/main/kotlin/com/chattriggers/ctjs/internal/utils/ResettableState.kt @@ -0,0 +1,10 @@ +package com.chattriggers.ctjs.internal.utils + +import gg.essential.elementa.state.BasicState + +class ResettableState(private val originalValue: T) : BasicState(originalValue) { + val isOriginalValue: Boolean + get() = get() === originalValue + + fun reset() = set(originalValue) +} diff --git a/src/main/resources/chattriggers.mixins.json b/src/main/resources/chattriggers.mixins.json index ad44393f..487cb788 100644 --- a/src/main/resources/chattriggers.mixins.json +++ b/src/main/resources/chattriggers.mixins.json @@ -11,11 +11,9 @@ "ChatHudAccessor", "ChatHudMixin", "ChatScreenAccessor", - "ChunkAccessor", "ClickableWidgetAccessor", "ClientChunkManagerAccessor", "ClientChunkMapAccessor", - "ClientConnectionMixin", "ClientPlayerEntityMixin", "ClientPlayerInteractionManagerMixin", "ClientWorldAccessor", @@ -27,37 +25,43 @@ "HandledScreenAccessor", "HandledScreenMixin", "InGameHudMixin", - "ItemStackMixin", "KeyBindingAccessor", - "LivingEntityMixin", "MinecraftClientMixin", "MouseMixin", - "NbtCompoundAccessor", "ParticleAccessor", "ParticleManagerMixin", - "PlayerEntityMixin", "PlayerListHudAccessor", "PlayerListHudMixin", - "PlayerScreenHandlerMixin", "RenderTickCounterMixin", - "ScreenHandlerMixin", - "SystemDetailsMixin", "commands.ClientCommandSourceMixin", "commands.ClientPlayNetworkHandlerMixin", - "commands.CommandContextAccessor", - "commands.CommandDispatcherMixin", - "commands.CommandNodeAccessor", - "commands.EntitySelectorAccessor", "sound.SoundAccessor", "sound.SoundManagerAccessor", "sound.SoundSystemAccessor", "sound.SoundSystemMixin", - "sound.SourceAccessor", - "stdio.BootstrapMixin", - "stdio.LoggerPrintStreamMixin" + "sound.SourceAccessor" ], "injectors": { "defaultRequire": 1 }, - "plugin": "com.chattriggers.ctjs.internal.launch.CTMixinPlugin" + "plugin": "com.chattriggers.ctjs.internal.launch.CTMixinPlugin", + "mixins": [ + "ChunkAccessor", + "ClientConnectionMixin", + "ItemStackMixin", + "LivingEntityMixin", + "NbtCompoundAccessor", + "PlayerEntityMixin", + "PlayerScreenHandlerMixin", + "Scoreboard$1Accessor", + "ScoreboardObjectiveMixin", + "ScreenHandlerMixin", + "SystemDetailsMixin", + "commands.CommandContextAccessor", + "commands.CommandDispatcherMixin", + "commands.CommandNodeAccessor", + "commands.EntitySelectorAccessor", + "stdio.BootstrapMixin", + "stdio.LoggerPrintStreamMixin" + ] }