|
1 | 1 | # Changelog |
2 | 2 |
|
3 | | -All notable changes to ZDiscord are documented here. The format is based on |
4 | | -[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project |
5 | | -adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
6 | | - |
7 | | -## [Unreleased] |
| 3 | +All notable changes to ZDiscord are documented here. |
8 | 4 |
|
9 | 5 | ## [1.2.0] - 2026-06-16 |
10 | 6 |
|
11 | 7 | ### Added |
12 | | -- **Developer API**: Clean public interface for third-party plugins (`ZDiscordAPI`, `ZDiscordProvider`, models, and Bukkit events like `ZDiscordPlayerLinkEvent`, `ZDiscordFollowEvent`, `ZDiscordStatUpdateEvent`). |
13 | | -- **Interactive Leaderboards**: `LeaderboardModule` completely rewritten with 🥇🥈🥉 medals, player head thumbnails, stat-specific styling, progress bars, pagination buttons, and a stat-switcher dropdown. |
14 | | -- **Persistent Leaderboard Panel**: Auto-updating leaderboard panel in a configurable channel, including a new "Most Followed Players" leaderboard. |
15 | | -- **Centralized Logger (`ZLogger`)**: 9 categories, 6 levels, compact format, and zero-cost debug via `Supplier`. Added a professional boxed `StartupBanner`. |
16 | | -- `/profile [player]` — renders a player card embed with avatar, NameMC link, first/last seen (Discord timestamps), sessions, playtime, advancement count, link status, follower count, online indicator, kills, deaths, and K/D ratio. Follow/Unfollow button on the embed subscribes the requester to DM notifications. |
17 | | -- `/seen <player>` — quick last-seen lookup. Returns online/offline status, last-seen timestamp, playtime, and session count. |
18 | | -- `/following` — lists the Minecraft players the requester follows. |
19 | | -- `/unfollow <player>` — stops following a player (previously only the button on `/profile`). |
20 | | -- `/confess <message>` — posts an anonymous confession to `channels.confessions`. Rate-limited (configurable via `confessions.cooldown`, default 300s). Stable monotonic handle per confessor. `&` colour codes convert to Discord markdown. |
21 | | -- Silent update check every 5 hours (was 6). One quiet embed to `misc.update-channel`, no pings. `misc.update-silent` suppresses both the in-game banner and Discord notice. |
22 | | -- Achievement rarity display — "First of the day" badge when the player is the first to unlock that advancement in 24 hours, and "Rare — only N% of players have this" badge below a configurable threshold (default 25%). |
23 | | -- Player activity storage: per-player `last_seen`, `first_join`, `sessions`, advancement unlocks, follow relationships. YAML uses `player_activity.yml`, `advancement_unlocks.yml`, `player_follows.yml`; MySQL uses three new tables. |
24 | | -- `ColorUtil.toDiscordMarkdown(text)` converts `&l` bold, `&o` italic, `&n` underline, `&m` strikethrough to Discord markdown. Colour codes are dropped. Applied to all player-visible embed text. |
25 | | -- `FollowModule` — in-memory player-to-follower cache with non-blocking DM dispatch via `User.openPrivateChannel()`. Cache populates lazily on first join, refreshes on add/remove. |
26 | | -- `LeaderboardModule.getStat(uuid, stat)` for per-player stat lookups. |
27 | | -- `PlayerProfileBuilder` — single source of truth for the profile-card embed. Resolves linked Discord usernames. |
28 | | -- `confessions.color` (default `#9B59B6`) and `confessions.cooldown` (seconds). |
29 | | -- `events.advancement.show-rarity` (default true) and `events.advancement.rarity-threshold` (default 0.25). |
30 | | -- `follow.enabled` config toggle. |
31 | | -- MySQL `isFollowing` uses `SELECT COUNT(*) > 0` instead of fetching all followers. |
| 8 | +- Developer API (`ZDiscordAPI`, `ZDiscordProvider`, Bukkit events) for third-party plugins. |
| 9 | +- Interactive leaderboards with medals, head thumbnails, pagination, and stat-switcher dropdown. |
| 10 | +- Auto-updating leaderboard panel in a configurable channel. |
| 11 | +- Centralized logger (`ZLogger`) with 9 categories, 6 levels, compact format. |
| 12 | +- `/profile [player]` — player card with avatar, NameMC link, stats, and follow button. |
| 13 | +- `/seen <player]` — last-seen lookup with online status, playtime, sessions. |
| 14 | +- `/following` and `/unfollow <player>` — manage follow subscriptions from Discord. |
| 15 | +- `/confess <message>` — anonymous confessions with cooldown and configurable color. |
| 16 | +- Achievement rarity badges ("First of the day", rare advancement threshold). |
| 17 | +- Player activity storage (last_seen, first_join, sessions, advancement unlocks, follows). |
| 18 | +- `ColorUtil.toDiscordMarkdown()` for `&l`, `&o`, `&n`, `&m` conversion. |
| 19 | +- `FollowModule` — in-memory cache with non-blocking DM dispatch. |
| 20 | +- `PlayerProfileBuilder` for profile card embeds with Discord username resolution. |
32 | 21 |
|
33 | 22 | ### Changed |
34 | | -- `config-version` bumped to 4. Default `avatar-url` changed to `mc-heads.net` for higher reliability and uptime compared to `crafatar.com`. |
35 | | -- `HeadUtil` rewritten to support `mc-heads.net` with `avatar()`, `body()`, and `combo()` methods and various size constants. |
36 | | -- `YamlStorage` and `MySQLStorage` gained three new files / three new tables each, plus 12 new `StorageManager` methods. |
37 | | -- `AdvancementListener` persists the unlock to storage, reads rarity stats asynchronously, and guards against duplicate events (e.g. from `/reload`). Duplicate events suppress the generic "Unlocked by" field. |
38 | | -- `UpdateChecker` interval changed from 6 to 5 hours. `postSilentDiscordNotice()` fires once per detected release via `AtomicBoolean` guard and respects `misc.update-silent`. |
39 | | -- `JoinQuitListener` writes `last_seen` and increments sessions on every join/quit, keeping timestamps fresh after abnormal disconnects. |
40 | | -- First-join detection uses storage (`getFirstJoin(uuid) == 0`) instead of `player.hasPlayedBefore()`, which breaks after `/reload`. |
41 | | -- `ColorUtil.stripColor` used only internally for pre-processing before `toDiscordMarkdown`. |
| 23 | +- `config-version` bumped to 4. Default avatar changed to mc-heads.net. |
| 24 | +- `HeadUtil` rewritten for mc-heads.net with `avatar()`, `body()`, `combo()`. |
| 25 | +- Storage backends gained 12 new methods for activity, advancements, and follows. |
| 26 | +- `AdvancementListener` persists unlocks, reads rarity stats async, guards against duplicates. |
| 27 | +- `UpdateChecker` interval 6h → 5h. Silent Discord notice fires once per release. |
| 28 | +- `JoinQuitListener` writes last_seen and increments sessions on every join/quit. |
| 29 | +- First-join detection uses storage instead of `player.hasPlayedBefore()`. |
42 | 30 |
|
43 | 31 | ### Fixed |
44 | | -- `SetupCommand` bug causing "Loading options failed" fixed (StringSelectMenu `addOption` parameter order corrected). |
45 | | -- `SetupCommand` NPE when a selected ticket category was removed from config fixed. |
46 | | -- `JoinQuitListener` null-safe bot connection checks added to prevent NPEs when the bot is disconnected. Playtime session tracking now records join timestamp before checking bot status. |
47 | | -- `/seen` avatar resolution fixed to use the new `HeadUtil.avatar()`. |
48 | | -- `FollowModule.onPlayerJoin` no longer blocks the scheduler thread — `retrieveUserById().complete()` replaced with `.queue()`. |
| 32 | +- `SetupCommand` "Loading options failed" bug and NPE on removed ticket categories. |
| 33 | +- `JoinQuitListener` null-safe bot connection checks. |
| 34 | +- `/seen` avatar resolution and "never joined" message. |
| 35 | +- `FollowModule.onPlayerJoin` no longer blocks scheduler thread. |
49 | 36 | - `MySQLStorage.isFollowing` uses `SELECT COUNT(*)` instead of fetching all followers. |
50 | | -- `/seen` shows "This player has never joined the server" instead of "No activity recorded yet." |
51 | | -- Follow DM notification includes a Discord timestamp of when the player joined. |
52 | | -- Profile card Discord section shows the actual username instead of the raw numeric ID. |
53 | | -- Confession handles use a monotonic `AtomicInteger` counter instead of `hash % 10000`. |
54 | | -- `/panel` no longer crashes when used in a thread or forum channel. |
55 | | -- `UpdateChecker` Discord announcement retries on failure instead of being permanently lost. |
56 | | -- Confession cooldown applies only after successful send. |
| 37 | +- Profile card shows actual Discord username instead of raw ID. |
| 38 | +- Confession handles use monotonic counter instead of `hash % 10000`. |
| 39 | +- `/panel` no longer crashes in thread or forum channels. |
| 40 | +- `UpdateChecker` Discord announcement retries on failure. |
57 | 41 |
|
58 | 42 | ## [1.1.0] - 2026-06-06 |
59 | 43 |
|
60 | 44 | ### Changed |
61 | | -- Version bumped to 1.1.0 across `pom.xml` and `plugin.yml`. |
62 | | -- `api-version` set to `1.20` to match the `paper-api 1.20.4` dependency. |
63 | | -- `config.yml` and `messages.yml` rewritten without marketing language, emoji, or boilerplate. |
64 | | -- Status embed centralised in `util/StatusEmbedBuilder.java` — shared by the status module, `/status` slash command, and `/setup` wizard. |
65 | | -- Avatar URL resolution centralised in `util/HeadUtil.java`, message templates in `util/PlaceholderUtil.java`. |
66 | | -- Ticket panel redesigned: `StringSelectMenu` dropdown of categories plus a "Quick Open" button. In-ticket message shows category, opener, and close/claim/transcript actions. |
67 | | -- Ticket button IDs consolidated to `zdiscord_create_ticket:<action>`. Button handler in `TicketButtonListener` handles close, claim, transcript, quick-open, and the category dropdown. |
68 | | -- Status embed rebuilt: full-width memory row with block-character progress bar, color-coded health indicator (green/amber/red), guild icon thumbnail. |
69 | | -- Performance embed rebuilt: Unicode block-character sparklines, per-row "TPS / Memory / Players+Threads" fields, healthy/warning/critical title, alert fields for low TPS and high memory. |
70 | | -- Join/quit embeds rebuilt with title, thumbnail, fields (player, online count, status), and footer from configured templates. |
71 | | -- `StatusModule` and `PerformanceModule` persist edited message IDs to `status_data.yml` / `performance_data.yml` instead of `config.yml`. |
72 | | -- `ConfigManager` exposes `getConfig()` for tests and callers needing raw Bukkit `FileConfiguration`. |
73 | | -- `TicketModule` exposes `loadCategories(root)` for unit-testing without JDA. |
74 | | -- JUnit 5 test suite in CI: config loading, ticket category parsing, status embed structure, update-checker version comparison, head URL resolution, YAML storage backend. |
75 | | -- Configurable ticket categories: `categories` list in `config.yml` (id, label, description, emoji, color). Panel renders from config, re-themeable without rebuilding. |
76 | | -- `tickets.panel.*` config block for panel embed title, description, color, thumbnail, image, and footer. |
77 | | -- `/panel` Discord slash command and `/zdiscord panel` in-game command. |
78 | | -- `tickets.panel-channel` config for pinning the panel channel. |
79 | | -- `/zdiscord update [check|dismiss]` admin subcommand. `check` triggers a Modrinth lookup; `dismiss` suppresses the join-banner. |
80 | | -- Clickable update notifications: `ClickEvent` (openUrl) with `HoverEvent` and dismiss shortcut. Semver comparison treats `1.2.3-beta` as older than `1.2.3`. |
81 | | -- UpdateChecker re-runs every 5 hours for long-running servers. |
82 | | -- Chat bridge listens for Paper's `AsyncChatEvent` (via `PaperChatListener`) and the legacy `AsyncPlayerChatEvent`. On Paper, the modern listener consumes the event to avoid double-sends. |
| 45 | +- Version bumped to 1.1.0. `api-version` set to `1.20`. |
| 46 | +- `config.yml` and `messages.yml` rewritten without marketing language. |
| 47 | +- Status embed centralised in `StatusEmbedBuilder`. |
| 48 | +- Avatar URLs centralised in `HeadUtil`, message templates in `PlaceholderUtil`. |
| 49 | +- Ticket panel redesigned: `StringSelectMenu` dropdown + "Quick Open" button. |
| 50 | +- Status embed: memory progress bar, color-coded health indicator, guild icon thumbnail. |
| 51 | +- Performance embed: Unicode sparklines, per-row TPS/Memory fields, alert fields. |
| 52 | +- Join/quit embeds rebuilt with title, thumbnail, fields, and footer. |
| 53 | +- `StatusModule` and `PerformanceModule` persist message IDs to dedicated YAML files. |
| 54 | +- `ConfigManager` exposes `getConfig()` for tests. |
| 55 | +- JUnit 5 test suite: config, tickets, status, update-checker, heads, YAML storage. |
| 56 | +- Configurable ticket categories (`tickets.categories` list). |
| 57 | +- `/panel` slash command and `/zdiscord panel` in-game command. |
| 58 | +- `/zdiscord update [check|dismiss]` with clickable notification and dismiss shortcut. |
| 59 | +- UpdateChecker re-runs every 5 hours. |
| 60 | +- Chat bridge listens for Paper's `AsyncChatEvent` and legacy `AsyncPlayerChatEvent`. |
83 | 61 |
|
84 | 62 | ### Fixed |
85 | | -- `ConfigManager` no longer overwrites user config on version bump — missing keys merge in, existing values stay, version stamp updates. |
86 | | -- `ConsoleModule` hooks only the server root logger and skips ZDiscord's own logger, fixing double-logging. |
87 | | -- `WebhookManager` replaces forbidden words (`discord`, `clyde`) with `Player` instead of asterisks (also rejected by Discord). Rate limiter uses `ScheduledExecutorService` instead of `Thread.sleep`. |
88 | | -- `BotManager.connect()` no longer requests the non-existent `CacheFlag.MEMBER_OVERRIDES`. |
89 | | -- `StaffChatModule` toggled-player set uses `ConcurrentHashMap.newKeySet()`. |
90 | | -- `TPSUtil` uses `volatile` lazy-init flag for cross-thread visibility. |
91 | | -- `ZDiscordCommand.handleDump` uses try-with-resources for the writer. |
92 | | -- `JoinQuitListener` no longer reports `-1` player count on quit (defers activity update by two ticks). |
93 | | -- `DiscordChatListener.handleConsoleCommand` requires `misc.console-role` (or Administrator) and rejects dangerous commands (`op`, `deop`, `stop`, `restart`, `reload confirm`, `whitelist remove`). |
94 | | -- `WebhookManager` no longer caches `null` on webhook creation failure — subsequent messages retry. |
95 | | -- Ticket creator count decrements when channel is closed from Discord (close handler resolves requester from permission overrides). |
96 | | -- `PlayerQuitEvent` no longer races the online-count update. |
| 63 | +- `ConfigManager` no longer overwrites user config on version bump. |
| 64 | +- `ConsoleModule` hooks only the server root logger (fixes double-logging). |
| 65 | +- `WebhookManager` replaces forbidden words with `Player` (Discord rejects asterisks). |
| 66 | +- `BotManager.connect()` no longer requests non-existent `CacheFlag.MEMBER_OVERRIDES`. |
| 67 | +- `StaffChatModule` uses `ConcurrentHashMap.newKeySet()`. |
| 68 | +- `TPSUtil` uses `volatile` lazy-init flag. |
| 69 | +- `ZDiscordCommand.handleDump` uses try-with-resources. |
| 70 | +- `JoinQuitListener` no longer reports -1 player count on quit. |
| 71 | +- `DiscordChatListener` requires `misc.console-role` and rejects dangerous commands. |
| 72 | +- `WebhookManager` retries on webhook creation failure. |
| 73 | +- Ticket creator count decrements on Discord-initiated close. |
97 | 74 | - `PlaceholderUtil` strips both `&` and `\u00A7` colour codes. |
98 | 75 | - `ColorUtil` handles null/empty/malformed hex. |
99 | 76 | - `HeadUtil` handles null names without NPE. |
100 | 77 |
|
101 | 78 | ### Added |
102 | | -- Apache 2.0 license headers on every Java source file. |
103 | | -- `ReconnectListener`, `TicketButtonListener`, `SetupCommand`, and `SlashCommandManager` registered as JDA event listeners (previously declared but never wired). |
104 | | -- `/setup` slash command: interactive module/channel/role wizard. |
| 79 | +- Apache 2.0 license headers on all Java source files. |
| 80 | +- `ReconnectListener`, `TicketButtonListener`, `SetupCommand`, `SlashCommandManager` wired as JDA listeners. |
| 81 | +- `/setup` slash command with interactive module/channel/role wizard. |
105 | 82 | - Discord role grant on link (`linking.linked-role`). |
106 | | -- Link reward commands via `PlaceholderUtil` (`%player%`, `%displayname%`, etc.). |
107 | | -- Ticket creator tracked via channel permission overrides. |
| 83 | +- Link reward commands via `PlaceholderUtil`. |
108 | 84 | - `UpdateChecker` connect/read timeouts and explicit UTF-8 charset. |
109 | 85 | - MySQL leak detection and validation timeout. |
110 | 86 | - `ReactionRoleModule` grants LuckPerms permissions via `lp user <uuid> permission set <node>`. |
111 | | -- `/zdiscord dump` writes a diagnostics file with version, platform, and module state. |
| 87 | +- `/zdiscord dump` writes diagnostics file with version, platform, and module state. |
112 | 88 |
|
113 | 89 | ### Removed |
114 | 90 | - Pre-built `ZDiscord-1.0.0-beta.jar` from the repo. |
115 | | -- `images/feature.png`, `images/logo.png`, `images/banner.png`. |
116 | | -- `.github/workflows/deployer-pipeline.yml` (allowed anyone pushing `workspace.zip` to overwrite the repo and self-commit). |
117 | | -- Fake SpotBugs / quality / benchmark steps from `.github/workflows/build.yml`. |
| 91 | +- `.github/workflows/deployer-pipeline.yml` (allowed anyone pushing `workspace.zip` to overwrite the repo). |
| 92 | +- Fake SpotBugs / quality / benchmark steps from build workflow. |
118 | 93 | - ASCII-art banner in `ZDiscord.onEnable`. |
119 | | -- "Premium" marketing language and `Premium Only: false` from `plugin.yml`. |
120 | | -- False "1.21+ Paper/Folia/Spigot" claim (actual dependency is 1.20.4). |
121 | | -- Dead bStats badge pointing to a different plugin. |
| 94 | +- "Premium" marketing language from `plugin.yml`. |
122 | 95 |
|
123 | 96 | ## [1.0.0] - Initial release |
124 | 97 |
|
|
0 commit comments