From dc45e2a741d9475b32f9adc3dccca6b5cc7b4c22 Mon Sep 17 00:00:00 2001 From: milanmalhotra Date: Wed, 19 Aug 2026 20:27:46 -0400 Subject: [PATCH] fix(metrics): count a command under the label actually typed Every invocation incremented both the command family under the canonical name and the alias family under the typed label, so one /sethome reported as a use of create-home as well. That made the alias charts useless for the thing they exist for, deciding which labels to retire, because a canonical chart could not be told apart from alias traffic. An invocation now increments exactly one family: the canonical name goes to command, anything else to alias. commands_total sums both families so it keeps meaning every invocation whatever label was typed. Expect a step down in the command_* lines on the dashboard where alias traffic used to be folded in. Chart ids are unchanged. --- .changeset/quiet-otters-count.md | 5 +++++ .../metrics/CommandUsageListener.java | 14 ++++++++----- .../sethomestwo/metrics/MetricsReporter.java | 3 ++- .../sethomestwo/metrics/WindowShare.java | 8 ++++--- .../metrics/CommandUsageListenerTest.java | 21 +++++++++++++------ .../sethomestwo/metrics/WindowShareTest.java | 14 +++++++++++++ 6 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 .changeset/quiet-otters-count.md diff --git a/.changeset/quiet-otters-count.md b/.changeset/quiet-otters-count.md new file mode 100644 index 0000000..abbede4 --- /dev/null +++ b/.changeset/quiet-otters-count.md @@ -0,0 +1,5 @@ +--- +bump: patch +--- + +Anonymous usage metrics now record a command under the exact label typed, so a command and its aliases are counted separately instead of both at once. diff --git a/src/main/java/com/samleighton/sethomestwo/metrics/CommandUsageListener.java b/src/main/java/com/samleighton/sethomestwo/metrics/CommandUsageListener.java index f2dafd7..0589258 100644 --- a/src/main/java/com/samleighton/sethomestwo/metrics/CommandUsageListener.java +++ b/src/main/java/com/samleighton/sethomestwo/metrics/CommandUsageListener.java @@ -12,9 +12,10 @@ import java.util.Locale; /** - * Counts every SetHomesTwo command typed by a player or the console, by - * canonical name and by the label actually typed. Commands owned by other - * plugins are ignored. Never cancels or alters the event. + * Counts every SetHomesTwo command typed by a player or the console, once, + * under the label actually typed: the canonical name goes to COMMAND and an + * alias to ALIAS, so the two can be retired on separate evidence. Commands + * owned by other plugins are ignored. Never cancels or alters the event. */ public class CommandUsageListener implements Listener { @@ -50,8 +51,11 @@ void count(String commandLine) { String typed = label.startsWith(namespace) ? label.substring(namespace.length()) : label; UsageCounters counters = plugin.getUsageCounters(); - counters.increment(UsageCounters.Family.COMMAND, command.getName()); - counters.increment(UsageCounters.Family.ALIAS, typed); + if (typed.equals(command.getName().toLowerCase(Locale.ROOT))) { + counters.increment(UsageCounters.Family.COMMAND, command.getName()); + } else { + counters.increment(UsageCounters.Family.ALIAS, typed); + } } catch (RuntimeException ignored) { // Counting is best effort. } diff --git a/src/main/java/com/samleighton/sethomestwo/metrics/MetricsReporter.java b/src/main/java/com/samleighton/sethomestwo/metrics/MetricsReporter.java index 4a44562..dcb8448 100644 --- a/src/main/java/com/samleighton/sethomestwo/metrics/MetricsReporter.java +++ b/src/main/java/com/samleighton/sethomestwo/metrics/MetricsReporter.java @@ -172,7 +172,8 @@ private static final class BStatsHandle implements AutoCloseable { metrics.addCustomChart(new SingleLineChart(commandChartId(command), () -> share.count(UsageCounters.Family.COMMAND, command))); } - metrics.addCustomChart(new SingleLineChart("commands_total", () -> share.total(UsageCounters.Family.COMMAND))); + metrics.addCustomChart(new SingleLineChart("commands_total", + () -> share.total(UsageCounters.Family.COMMAND, UsageCounters.Family.ALIAS))); for (String alias : declaredAliases(plugin)) { metrics.addCustomChart(new SingleLineChart(aliasChartId(alias), diff --git a/src/main/java/com/samleighton/sethomestwo/metrics/WindowShare.java b/src/main/java/com/samleighton/sethomestwo/metrics/WindowShare.java index fba42d4..bfaa744 100644 --- a/src/main/java/com/samleighton/sethomestwo/metrics/WindowShare.java +++ b/src/main/java/com/samleighton/sethomestwo/metrics/WindowShare.java @@ -45,10 +45,12 @@ int count(UsageCounters.Family family, String key) { return window(family).getOrDefault(key, 0); } - /** Sum of every key in the window. */ - int total(UsageCounters.Family family) { + /** Sum of every key in the window, across one family or several. */ + int total(UsageCounters.Family... families) { int sum = 0; - for (int value : window(family).values()) sum += value; + for (UsageCounters.Family family : families) { + for (int value : window(family).values()) sum += value; + } return sum; } diff --git a/src/test/java/com/samleighton/sethomestwo/metrics/CommandUsageListenerTest.java b/src/test/java/com/samleighton/sethomestwo/metrics/CommandUsageListenerTest.java index 4ffbec2..4b466c2 100644 --- a/src/test/java/com/samleighton/sethomestwo/metrics/CommandUsageListenerTest.java +++ b/src/test/java/com/samleighton/sethomestwo/metrics/CommandUsageListenerTest.java @@ -33,7 +33,7 @@ void aCanonicalCommandCountsUnderItsOwnName() { playerTypes("/go-home base"); assertEquals(1, commands().get("go-home")); - assertEquals(1, aliases().get("go-home")); + assertTrue(aliases().isEmpty(), "a canonical name is not an alias use"); } @Test @@ -45,21 +45,30 @@ void aDispatchedCommandIsCounted() { } @Test - void anAliasCountsUnderTheCanonicalNameAndTheTypedAlias() { + void anAliasCountsOnlyUnderTheTypedAlias() { playerTypes("/sethome base"); - assertEquals(1, commands().get("create-home")); assertEquals(1, aliases().get("sethome")); - assertTrue(!aliases().containsKey("create-home")); + assertTrue(commands().isEmpty(), "an alias use is not a use of the canonical name"); + } + + @Test + void aCommandAndItsAliasAreCountedSeparately() { + playerTypes("/create-home one"); + playerTypes("/sethome two"); + playerTypes("/sethome three"); + + assertEquals(1, commands().get("create-home")); + assertEquals(2, aliases().get("sethome")); } @Test void theNamespacedFormCountsOnceWithoutTheNamespace() { playerTypes("/sethomestwo:home base"); - assertEquals(1, commands().get("go-home")); assertEquals(1, aliases().get("home")); - assertEquals(1, commands().size()); + assertEquals(1, aliases().size()); + assertTrue(commands().isEmpty()); } @Test diff --git a/src/test/java/com/samleighton/sethomestwo/metrics/WindowShareTest.java b/src/test/java/com/samleighton/sethomestwo/metrics/WindowShareTest.java index e34c775..2f3c89f 100644 --- a/src/test/java/com/samleighton/sethomestwo/metrics/WindowShareTest.java +++ b/src/test/java/com/samleighton/sethomestwo/metrics/WindowShareTest.java @@ -6,6 +6,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import static com.samleighton.sethomestwo.metrics.UsageCounters.Family.ALIAS; import static com.samleighton.sethomestwo.metrics.UsageCounters.Family.COMMAND; import static com.samleighton.sethomestwo.metrics.UsageCounters.Family.GUI_ACTION; import static org.junit.jupiter.api.Assertions.assertArrayEquals; @@ -76,6 +77,19 @@ void anEmptyWindowIsStillOneWindow() { assertEquals(1, share.total(COMMAND)); } + @Test + void aTotalOverSeveralFamiliesAddsUpOneWindowOfEach() { + UsageCounters counters = new UsageCounters(); + counters.increment(COMMAND, "create-home"); + counters.increment(ALIAS, "sethome"); + counters.increment(ALIAS, "sethome"); + WindowShare share = share(counters); + + assertEquals(3, share.total(COMMAND, ALIAS)); + assertEquals(1, share.count(COMMAND, "create-home"), "each family is drained once, not twice"); + assertEquals(2, share.count(ALIAS, "sethome")); + } + @Test void familiesDoNotLeakIntoEachOther() { UsageCounters counters = new UsageCounters();