Skip to content

Show player activity ranking in info command output and unify command formatting#76

Merged
dmccoystephenson merged 8 commits intomainfrom
copilot/show-player-activity-ranking
Mar 8, 2026
Merged

Show player activity ranking in info command output and unify command formatting#76
dmccoystephenson merged 8 commits intomainfrom
copilot/show-player-activity-ranking

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 7, 2026

Adds activity ranking (e.g., Ranking: 3/15) to /at info output, showing where a player stands relative to all tracked players by total hours played. Updates formatting across all commands to use a consistent style with GOLD box-drawing borders, colored labels, and visual indicators. Adds a style guide documenting the formatting conventions for reuse in other projects.

Changes

  • PersistentData: Added getPlayerRank(UUID) — uses an O(n) single-pass algorithm that counts players with strictly greater hours, so tied players share the same rank
  • ActivityRecord.sendInfoToSender(): Now accepts rank and totalPlayers params; displays ranking line with a visual bar indicator. Formatting updated to use GOLD box-drawing borders (┌│└), YELLOW+BOLD player names, GRAY labels, and colored values (GREEN for play time, AQUA for ranking, GREEN/RED for online/offline status)
  • InfoCommand: Computes rank and total count before delegating to sendInfoToSender
  • HelpCommand: Updated to use GOLD box-drawing borders with AQUA commands and GRAY descriptions
  • DefaultCommand: Updated to use GOLD box-drawing borders with version in header, GRAY labels, and colored values
  • StatsCommand: Updated to use GOLD box-drawing borders with GRAY labels and GREEN values
  • TopCommand: Updated to use GOLD box-drawing borders with numbered entries and visual progress bars scaled to the top player
  • ListCommand: Updated to use GOLD box-drawing borders with numbered session entries and GREEN/RED Active/Ended status indicators
  • ConfigService.sendConfigList(): Updated to use GOLD box-drawing borders with each config option on its own line
  • COMMAND_OUTPUT_STYLE.md: Added style guide documenting the color scheme, box-drawing structure, visual progress bars, code patterns, and examples for reproducing the formatting in other projects
  • PersistentDataTest: 5 new tests covering single player, multi-player ordering, tied ranks, missing player, and null UUID

Example output

/at info

┌─ PlayerName ─ Activity Info
│ Logins:    42
│ Play Time: 156.30h
│ Ranking:   3/15 [███████░░░]
│ Status:    Online
│ Session:   1.25h since login
│ First Login: 2025-01-15T10:30:00
└─────────────────────────

/at top

┌─ Activity Tracker ─ Top Players
│ #1 PlayerA 156.30h [██████████]
│ #2 PlayerB 98.50h  [██████░░░░]
│ #3 PlayerC 45.20h  [██░░░░░░░░]
└─────────────────────────

/at stats

┌─ Activity Tracker ─ Statistics
│ Unique Players: 42
│ Total Logins:   156
└─────────────────────────
Original prompt

This section details on the original issue you should resolve

<issue_title>Show a player's activity ranking in the output of the info command.</issue_title>
<issue_description></issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copilot AI changed the title [WIP] Show a player's activity ranking in info command output Show player activity ranking in info command output Mar 7, 2026
@dmccoystephenson dmccoystephenson requested a review from Copilot March 7, 2026 04:03
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an activity ranking line to /at info output so players can see their position among all tracked players by total hours played.

Changes:

  • Added PersistentData.getPlayerRank(UUID) to compute a 1-based rank by total hours played.
  • Updated /at info flow to compute (rank, totalPlayers) and display Activity Ranking: X/Y.
  • Added unit tests covering rank behavior (single player, ordering, missing player, null UUID).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
src/main/java/dansplugins/activitytracker/data/PersistentData.java Adds getPlayerRank(UUID) based on sorting records by total hours.
src/main/java/dansplugins/activitytracker/objects/ActivityRecord.java Extends sendInfoToSender to accept (rank, totalPlayers) and prints the ranking line.
src/main/java/dansplugins/activitytracker/commands/InfoCommand.java Computes rank + total player count and passes them into sendInfoToSender.
src/test/java/dansplugins/activitytracker/data/PersistentDataTest.java Adds tests validating rank calculation and edge cases.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Collections.sort(sortedRecords, new Comparator<ActivityRecord>() {
@Override
public int compare(ActivityRecord r1, ActivityRecord r2) {
return Double.compare(r2.getTotalHoursSpent(), r1.getTotalHoursSpent());
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getPlayerRank sorts by total hours only; when multiple players have identical getTotalHoursSpent() values, the returned rank depends on the existing insertion/load order (which can change across restarts). Consider adding a deterministic tie-breaker (e.g., secondary compare on UUID) or explicitly defining tie behavior (shared rank vs. sequential).

Suggested change
return Double.compare(r2.getTotalHoursSpent(), r1.getTotalHoursSpent());
int hoursComparison = Double.compare(r2.getTotalHoursSpent(), r1.getTotalHoursSpent());
if (hoursComparison != 0) {
return hoursComparison;
}
// Deterministic tie-breaker: order by UUID when total hours are equal
return r1.getPlayerUUID().compareTo(r2.getPlayerUUID());

Copilot uses AI. Check for mistakes.
Comment on lines +127 to +140
ArrayList<ActivityRecord> sortedRecords = new ArrayList<>(activityRecords);
Collections.sort(sortedRecords, new Comparator<ActivityRecord>() {
@Override
public int compare(ActivityRecord r1, ActivityRecord r2) {
return Double.compare(r2.getTotalHoursSpent(), r1.getTotalHoursSpent());
}
});

for (int i = 0; i < sortedRecords.size(); i++) {
if (sortedRecords.get(i).getPlayerUUID().equals(playerUUID)) {
return i + 1;
}
}
return -1;
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getPlayerRank copies and sorts the entire activityRecords list on each call (O(n log n)). Since /at info may be run often on larger servers, consider computing rank in a single pass (count how many records have strictly greater hours) to avoid allocating/copying/sorting.

Suggested change
ArrayList<ActivityRecord> sortedRecords = new ArrayList<>(activityRecords);
Collections.sort(sortedRecords, new Comparator<ActivityRecord>() {
@Override
public int compare(ActivityRecord r1, ActivityRecord r2) {
return Double.compare(r2.getTotalHoursSpent(), r1.getTotalHoursSpent());
}
});
for (int i = 0; i < sortedRecords.size(); i++) {
if (sortedRecords.get(i).getPlayerUUID().equals(playerUUID)) {
return i + 1;
}
}
return -1;
// First, find the target player's total hours
double targetTotalHours = -1.0;
for (ActivityRecord record : activityRecords) {
if (record.getPlayerUUID().equals(playerUUID)) {
targetTotalHours = record.getTotalHoursSpent();
break;
}
}
// If the player does not have an activity record, return -1
if (targetTotalHours < 0.0) {
return -1;
}
// Count how many players have strictly greater total hours
int countGreater = 0;
for (ActivityRecord record : activityRecords) {
if (record.getTotalHoursSpent() > targetTotalHours) {
countGreater++;
}
}
// Rank is 1 plus the number of players with more hours
return countGreater + 1;

Copilot uses AI. Check for mistakes.
@dmccoystephenson
Copy link
Copy Markdown
Member

@copilot address comments

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 7, 2026

@copilot address comments

Addressed both review comments in 05055ed:

  • Replaced the O(n log n) sort-based approach with an O(n) single-pass that counts how many players have strictly greater hours
  • This also naturally handles ties — players with identical hours share the same rank
  • Added a test case validating tied-rank behavior

@dmccoystephenson
Copy link
Copy Markdown
Member

@copilot That works! However, the output is ugly. Update formatting for the /at info command to be like the output of the /at average command.

Copilot AI and others added 2 commits March 7, 2026 04:18
…x-drawing chars, colors, and rank bar

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 7, 2026

@copilot That works! However, the output is ugly. Update formatting for the /at info command to be like the output of the /at average command.

Updated in 760515b. The /at info output now matches the /at average style:

  • GOLD box-drawing borders (┌│└) instead of === lines
  • YELLOW+BOLD player name in the header
  • GRAY labels with colored values (GREEN for play time, AQUA for ranking, GREEN/RED for online/offline status)
  • Visual rank bar using Unicode block characters showing position relative to all players

Example output:

┌─ PlayerName ─ Activity Info
│ Logins:    42
│ Play Time: 156.30h
│ Ranking:   3/15 [███████░░░]
│ Status:    Online
│ Session:   1.25h since login
│ First Login: 2025-01-15T10:30:00
└─────────────────────────

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • hub.spigotmc.org
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/Activity-Tracker/Activity-Tracker org.codehaus.plexus.classworlds.launcher.Launcher compile -q (dns block)
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/Activity-Tracker/Activity-Tracker org.codehaus.plexus.classworlds.launcher.Launcher -f pom.xml -B -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip (dns block)
  • jitpack.io
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/Activity-Tracker/Activity-Tracker org.codehaus.plexus.classworlds.launcher.Launcher compile -q (dns block)
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/Activity-Tracker/Activity-Tracker org.codehaus.plexus.classworlds.launcher.Launcher -f pom.xml -B -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@dmccoystephenson dmccoystephenson marked this pull request as ready for review March 8, 2026 09:19
@dmccoystephenson
Copy link
Copy Markdown
Member

@copilot Go ahead and update the formatting for the rest of the commands to be like the output of /at average and /at info

Copilot AI and others added 2 commits March 8, 2026 09:22
…ox-drawing borders and colors

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
…y note, average params

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 8, 2026

@copilot Go ahead and update the formatting for the rest of the commands to be like the output of /at average and /at info

Updated all remaining commands in ea2d014. Every command now uses the same formatting style:

/at (default)

┌─ Activity Tracker ─ v1.3.0
│ Author: Daniel McCoy Stephenson
│ Wiki:   github.com/Dans-Plugins/Activity-Tracker/wiki
└─────────────────────────

/at help

┌─ Activity Tracker ─ Commands
│ /at help - View a list of helpful commands.
│ /at info - View your activity record.
│ /at info (playerName) - View a player's activity record.
│ /at list - View the 10 most recent sessions (admin only).
│ /at top - View the most active players.
│ /at stats - View activity stats for the server.
│ /at average [player] [days] - Avg daily activity (default: 7 days).
│ /at config - Show or set config options.
└─────────────────────────

/at stats

┌─ Activity Tracker ─ Statistics
│ Unique Players: 42
│ Total Logins:   156
└─────────────────────────

/at top — now includes visual bars scaled to the top player's hours

┌─ Activity Tracker ─ Top Players
│ #1 PlayerA 156.30h [██████████]
│ #2 PlayerB 98.50h  [██████░░░░]
│ #3 PlayerC 45.20h  [██░░░░░░░░]
└─────────────────────────

/at list — sessions with colored Active/Ended status

┌─ Activity Tracker ─ Recent Sessions (5)
│ #1 PlayerA - 2025-03-07 10:30:00 (Active)
│ #2 PlayerB - 2025-03-07 09:15:00 (Ended - 45.0 min)
└─────────────────────────

/at config show

┌─ Activity Tracker ─ Config
│ version:        1.3.0
│ debugMode:      false
│ restApiEnabled: false
│ restApiPort:    8080
└─────────────────────────

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • hub.spigotmc.org
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/Activity-Tracker/Activity-Tracker org.codehaus.plexus.classworlds.launcher.Launcher -f pom.xml -B -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip (dns block)
  • jitpack.io
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/Activity-Tracker/Activity-Tracker org.codehaus.plexus.classworlds.launcher.Launcher -f pom.xml -B -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title Show player activity ranking in info command output Show player activity ranking in info command output and unify command formatting Mar 8, 2026
@dmccoystephenson
Copy link
Copy Markdown
Member

@copilot Document command output formatting style so that it can be reproduced in other projects

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 8, 2026

@copilot Document command output formatting style so that it can be reproduced in other projects

Added COMMAND_OUTPUT_STYLE.md in cafbf4b. It covers:

  • Box-drawing character reference (┌│└─) with Unicode codepoints
  • Complete color scheme table mapping each element type to its ChatColor
  • Code patterns for headers, label-value lines, numbered lists, command lists, status indicators, and footers
  • Visual progress bar implementation with scaling examples for both value-based and rank-based bars
  • Label alignment conventions
  • Example output for every command type (info, stats, leaderboard, help, config, session list)
  • Notes on adapting the style for other projects

@dmccoystephenson dmccoystephenson merged commit 9cf88cc into main Mar 8, 2026
3 checks passed
@dmccoystephenson dmccoystephenson deleted the copilot/show-player-activity-ranking branch March 8, 2026 09:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Show a player's activity ranking in the output of the info command.

3 participants