Skip to content

Commit b24955c

Browse files
authored
refactor: debug handling and added a report command (#90)
1 parent f21d741 commit b24955c

25 files changed

Lines changed: 789 additions & 145 deletions

.github/ISSUE_TEMPLATE/bug-report.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ body:
111111
id: server-version
112112
attributes:
113113
label: Server version
114-
placeholder: Paper 1.21.11 build ...
114+
placeholder: Paper 26.1.2 build ...
115115
validations:
116116
required: true
117117

@@ -128,7 +128,7 @@ body:
128128
id: client-version
129129
attributes:
130130
label: Minecraft client version
131-
placeholder: 1.21.11
131+
placeholder: 26.1.2
132132
validations:
133133
required: false
134134

.github/workflows/promote-release.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,6 @@ jobs:
319319
paper
320320
folia
321321
game-versions: |-
322-
1.21.x
323322
26.1.x
324323
26.2.x
325324
files: ${{ steps.asset.outputs.jar }}
@@ -342,5 +341,5 @@ jobs:
342341
]
343342
platform_dependencies: |-
344343
{
345-
"PAPER": ["1.21.x", "26.1.x", "26.2.x"]
344+
"PAPER": ["26.1.x", "26.2.x"]
346345
}

.github/workflows/publish-marketplaces.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ jobs:
100100
paper
101101
folia
102102
game-versions: |-
103-
1.21.x
104103
26.1.x
104+
26.2.x
105105
files: ${{ steps.asset.outputs.jar }}
106106

107107
- name: Publish to Hangar
@@ -121,5 +121,5 @@ jobs:
121121
]
122122
platform_dependencies: |-
123123
{
124-
"PAPER": ["1.21.x", "26.1.x"]
124+
"PAPER": ["26.1.x", "26.2.x"]
125125
}

headdb-platforms/headdb-paper/src/main/java/io/github/silentdevelopment/headdb/paper/HeadDBPlugin.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import io.github.silentdevelopment.headdb.paper.message.Messages;
3131
import io.github.silentdevelopment.headdb.paper.metrics.HeadDBMetrics;
3232
import io.github.silentdevelopment.headdb.paper.prompt.PromptInputService;
33+
import io.github.silentdevelopment.headdb.paper.runtime.PlatformRequirements;
3334
import io.github.silentdevelopment.headdb.paper.runtime.PluginRuntime;
3435
import io.github.silentdevelopment.headdb.paper.runtime.RuntimeDiagnostics;
3536
import io.github.silentdevelopment.headdb.paper.runtime.StartupChecks;
@@ -69,6 +70,11 @@ public void onEnable() {
6970
return;
7071
}
7172

73+
if (!PlatformRequirements.supported(this)) {
74+
getServer().getPluginManager().disablePlugin(this);
75+
return;
76+
}
77+
7278
try {
7379
reload();
7480

headdb-platforms/headdb-paper/src/main/java/io/github/silentdevelopment/headdb/paper/command/RootCommand.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import io.github.silentdevelopment.headdb.paper.command.subcommand.RandomCommand;
1717
import io.github.silentdevelopment.headdb.paper.command.subcommand.RefreshCommand;
1818
import io.github.silentdevelopment.headdb.paper.command.subcommand.ReloadCommand;
19+
import io.github.silentdevelopment.headdb.paper.command.subcommand.ReportCommand;
1920
import io.github.silentdevelopment.headdb.paper.command.subcommand.StatusCommand;
2021
import io.github.silentdevelopment.headdb.paper.command.subcommand.TagsCommand;
2122
import io.github.silentdevelopment.headdb.paper.command.subcommand.UpdateCommand;
@@ -47,6 +48,7 @@ public RootCommand(@NotNull HeadDBPlugin plugin) {
4748
new VersionCommand(plugin),
4849
new StatusCommand(plugin),
4950
new DebugCommand(plugin),
51+
new ReportCommand(plugin),
5052
new VerifyCommand(plugin),
5153
new RefreshCommand(plugin),
5254
new ReloadCommand(plugin),

headdb-platforms/headdb-paper/src/main/java/io/github/silentdevelopment/headdb/paper/command/format/HelpFormatter.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ private HelpFormatter() {
3636
addSection(lines, sender, new HelpSection("General", List.of(
3737
new HelpEntry(List.of("help"), "h", List.of(), "/hdb help", "Show this command reference.", Permissions.HELP),
3838
new HelpEntry(List.of("version"), null, List.of(), "/hdb version", "Show version and build information.", Permissions.VERSION),
39-
new HelpEntry(List.of("open"), "o", List.of(), "/hdb open", "Open the main HeadDB GUI.", Permissions.OPEN)
39+
new HelpEntry(List.of("open"), "o", List.of(), "/hdb open", "Open the main GUI.", Permissions.OPEN)
4040
)));
4141

4242
addSection(lines, sender, new HelpSection("Heads", List.of(
4343
new HelpEntry(List.of("info"), "i", args(optional("id")), "/hdb info ", "Inspect a head by ID or held item.", Permissions.INFO),
44-
new HelpEntry(List.of("give"), "g", args(required("id"), optional("player"), optional("amount")), "/hdb give ", "Give a HeadDB item.", Permissions.GIVE),
44+
new HelpEntry(List.of("give"), "g", args(required("id"), optional("player"), optional("amount")), "/hdb give ", "Give a head.", Permissions.GIVE),
4545
new HelpEntry(List.of("player"), "p", args(required("name|uuid"), optional("player"), optional("amount")), "/hdb player ", "Give a player head.", Permissions.PLAYER),
46-
new HelpEntry(List.of("random"), "rnd", args(optional("amount"), optional("category"), optional("player")), "/hdb random ", "Give a random HeadDB item.", Permissions.GIVE)
46+
new HelpEntry(List.of("random"), "rnd", args(optional("amount"), optional("category"), optional("player")), "/hdb random ", "Give a random head.", Permissions.GIVE)
4747
)));
4848

4949
addSection(lines, sender, new HelpSection("Browse", List.of(
@@ -73,7 +73,8 @@ private HelpFormatter() {
7373

7474
addSection(lines, sender, new HelpSection("Database", List.of(
7575
new HelpEntry(List.of("status"), "st", List.of(), "/hdb status", "Show database state and counts.", Permissions.STATUS),
76-
new HelpEntry(List.of("debug"), "d", List.of(), "/hdb debug", "Show detailed runtime diagnostics.", Permissions.DEBUG),
76+
new HelpEntry(List.of("debug"), "d", List.of(), "/hdb debug", "Show concise runtime diagnostics.", Permissions.DEBUG),
77+
new HelpEntry(List.of("report"), "rpt", List.of(), "/hdb report", "Create a full support report.", Permissions.REPORT),
7778
new HelpEntry(List.of("verify"), "v", List.of(), "/hdb verify", "Verify the public remote without replacing the active database.", Permissions.VERIFY),
7879
new HelpEntry(List.of("refresh"), "ref", List.of(), "/hdb refresh", "Fetch the latest remote database.", Permissions.REFRESH),
7980
new HelpEntry(List.of("reload"), "rl", List.of(), "/hdb reload", "Reload config, messages, and runtime.", Permissions.RELOAD)

headdb-platforms/headdb-paper/src/main/java/io/github/silentdevelopment/headdb/paper/command/format/StatusFormatter.java

Lines changed: 123 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
import io.github.silentdevelopment.headdb.database.DatabaseStats;
44
import io.github.silentdevelopment.headdb.database.DatabaseStatus;
5+
import io.github.silentdevelopment.headdb.paper.HeadDBPlugin;
56
import io.github.silentdevelopment.headdb.paper.permission.Permissions;
67
import io.github.silentdevelopment.headdb.paper.runtime.RefreshState;
7-
import io.github.silentdevelopment.headdb.paper.runtime.PluginRuntime;
88
import net.kyori.adventure.text.Component;
99
import net.kyori.adventure.text.event.ClickEvent;
1010
import net.kyori.adventure.text.event.HoverEvent;
@@ -16,6 +16,7 @@
1616
import java.time.Instant;
1717
import java.time.ZoneId;
1818
import java.time.format.DateTimeFormatter;
19+
import java.util.ArrayList;
1920
import java.util.List;
2021
import java.util.Objects;
2122

@@ -26,77 +27,126 @@ public final class StatusFormatter {
2627
private StatusFormatter() {
2728
}
2829

29-
public static @NotNull List<Component> format(@NotNull PluginRuntime runtime, @NotNull CommandSender sender) {
30-
Objects.requireNonNull(runtime, "runtime");
30+
public static @NotNull List<Component> format(@NotNull HeadDBPlugin plugin, @NotNull CommandSender sender) {
31+
Objects.requireNonNull(plugin, "plugin");
3132
Objects.requireNonNull(sender, "sender");
3233

33-
DatabaseStatus status = runtime.database().status();
34-
DatabaseStats stats = runtime.database().stats();
35-
RefreshState refresh = runtime.refreshState();
36-
37-
return List.of(
38-
Component.empty(),
39-
Component.text("> ", NamedTextColor.GRAY).append(Component.text("HeadDB Status", NamedTextColor.RED)),
40-
databaseStatusLine(status, sender),
41-
line("Source", status.source()),
42-
line("Manifest ID", value(status.manifestId())),
43-
line("Catalog ID", value(status.artifactId())),
44-
line("Loaded at", formatInstant(status.loadedAt())),
45-
Component.empty(),
46-
line("Heads", stats.heads()),
47-
line("Categories", stats.categories()),
48-
line("Tags", stats.tags()),
49-
line("Collections", stats.collections()),
50-
line("Revocations", stats.revocations()),
51-
Component.empty(),
52-
line("Refresh running", yesNo(refresh.running())),
53-
line("Last failure", formatFailure(refresh.lastFailureMessage())),
54-
Component.empty()
55-
);
34+
DatabaseStatus status = plugin.runtime().database().status();
35+
DatabaseStats remoteStats = plugin.runtime().database().stats();
36+
RefreshState refresh = plugin.runtime().refreshState();
37+
38+
int hiddenHeads = plugin.headRegistry().hiddenHeads().size();
39+
int moreHeads = plugin.headRegistry().customHeads().list().size();
40+
int overrides = plugin.headRegistry().overrides().list().size();
41+
int playerHeads = plugin.headRegistry().playerHeads().knownPlayers().size();
42+
int moreCategories = plugin.customCategories().list().size();
43+
44+
List<Component> lines = new ArrayList<>();
45+
lines.add(Component.empty());
46+
lines.add(Component.text("> ", NamedTextColor.DARK_GRAY).append(Component.text("Status", NamedTextColor.RED)));
47+
lines.add(databaseLine(status));
48+
lines.add(line("Heads", remoteStats.heads()));
49+
lines.add(line("Hidden Heads", hiddenHeads));
50+
lines.add(line("More Heads", moreHeads));
51+
lines.add(line("Player Heads", playerHeads));
52+
lines.add(line("Categories", remoteStats.categories()));
53+
lines.add(line("More Categories", moreCategories));
54+
lines.add(line("Tags", remoteStats.tags()));
55+
lines.add(line("Collections", remoteStats.collections()));
56+
lines.add(line("Revocations", remoteStats.revocations()));
57+
lines.add(line("Overrides", overrides));
58+
lines.add(refreshLine(refresh, sender));
59+
lines.add(lastRefreshLine(refresh));
60+
61+
String failure = firstPresent(status.lastError(), refresh.lastFailureMessage());
62+
if (failure != null) {
63+
lines.add(line("Last error", failure));
64+
}
65+
66+
addSupportLine(lines, sender);
67+
lines.add(Component.empty());
68+
return List.copyOf(lines);
5669
}
5770

58-
private static @NotNull Component databaseStatusLine(@NotNull DatabaseStatus status, @NotNull CommandSender sender) {
59-
Component line = Component.text("Status: ", NamedTextColor.GRAY).append(Component.text(String.valueOf(status.state()), statusColor(status)));
71+
private static @NotNull Component databaseLine(@NotNull DatabaseStatus status) {
72+
Component line = Component.text("Database: ", NamedTextColor.GRAY).append(Component.text(String.valueOf(status.state()), statusColor(status)));
73+
String source = value(status.source());
6074

61-
if (isLoaded(status)) {
62-
return line;
75+
if (!source.equals("none")) {
76+
line = line.append(Component.text(" from ", NamedTextColor.GRAY)).append(Component.text(source, NamedTextColor.GOLD));
6377
}
6478

65-
if (!Permissions.has(sender, Permissions.REFRESH)) {
66-
return line;
79+
return line;
80+
}
81+
82+
private static @NotNull Component refreshLine(@NotNull RefreshState refresh, @NotNull CommandSender sender) {
83+
String text = refresh.running() ? "running " + refresh.currentOperation() : "idle";
84+
Component line = line("Refresh", text);
85+
86+
if (!refresh.running() && Permissions.has(sender, Permissions.REFRESH)) {
87+
line = line.append(Component.text(" ")).append(refreshButton());
6788
}
6889

69-
return line.append(Component.text(" ")).append(refreshButton());
90+
return line;
7091
}
7192

72-
private static boolean isLoaded(@NotNull DatabaseStatus status) {
73-
return "LOADED".equalsIgnoreCase(String.valueOf(status.state()));
74-
}
93+
private static @NotNull Component lastRefreshLine(@NotNull RefreshState refresh) {
94+
if (refresh.lastOutcome() == RefreshState.RefreshOutcome.SUCCESS) {
95+
return line("Last Refresh", refresh.lastOperation() + " completed at " + formatInstant(refresh.lastSuccessfulRefresh()));
96+
}
7597

76-
private static @NotNull NamedTextColor statusColor(@NotNull DatabaseStatus status) {
77-
if (isLoaded(status)) {
78-
return NamedTextColor.GOLD;
98+
if (refresh.lastOutcome() == RefreshState.RefreshOutcome.FAILURE) {
99+
return line("Last Refresh", refresh.lastOperation() + " failed at " + formatInstant(refresh.lastFailedRefresh()));
79100
}
80101

81-
return NamedTextColor.RED;
102+
return line("Last Refresh", "never");
82103
}
83104

84105
private static @NotNull Component refreshButton() {
85-
return Component.text("[ ", NamedTextColor.DARK_GRAY)
86-
.append(Component.text("REFRESH", NamedTextColor.GOLD).clickEvent(ClickEvent.runCommand("/hdb refresh")).hoverEvent(HoverEvent.showText(Component.text("Click to refresh the HeadDB database.", NamedTextColor.GRAY))))
87-
.append(Component.text(" ]", NamedTextColor.DARK_GRAY));
106+
return Component.text("[ ", NamedTextColor.DARK_GRAY).append(Component.text("REFRESH", NamedTextColor.GOLD).clickEvent(ClickEvent.runCommand("/hdb refresh")).hoverEvent(HoverEvent.showText(Component.text("Click to refresh the database.", NamedTextColor.GRAY)))).append(Component.text(" ]", NamedTextColor.DARK_GRAY));
88107
}
89108

90-
private static @NotNull Component line(@NotNull String key, @Nullable Object value) {
91-
return Component.text(key + ": ", NamedTextColor.GRAY).append(Component.text(String.valueOf(value), NamedTextColor.GOLD));
109+
private static void addSupportLine(@NotNull List<Component> lines, @NotNull CommandSender sender) {
110+
boolean canDebug = Permissions.has(sender, Permissions.DEBUG);
111+
boolean canReport = Permissions.has(sender, Permissions.REPORT);
112+
113+
if (!canDebug && !canReport) {
114+
return;
115+
}
116+
117+
Component line = Component.text("Support: ", NamedTextColor.GRAY);
118+
119+
if (canDebug) {
120+
line = line.append(Component.text("/hdb debug", NamedTextColor.GOLD));
121+
}
122+
123+
if (canDebug && canReport) {
124+
line = line.append(Component.text(" | ", NamedTextColor.DARK_GRAY));
125+
}
126+
127+
if (canReport) {
128+
line = line.append(Component.text("/hdb report", NamedTextColor.GOLD));
129+
}
130+
131+
lines.add(line);
92132
}
93133

94-
private static @NotNull String yesNo(boolean value) {
95-
if (value) {
96-
return "yes";
134+
private static @NotNull NamedTextColor statusColor(@NotNull DatabaseStatus status) {
135+
String state = String.valueOf(status.state());
136+
137+
if ("LOADED".equalsIgnoreCase(state)) {
138+
return NamedTextColor.GOLD;
139+
}
140+
141+
if ("LOADING".equalsIgnoreCase(state)) {
142+
return NamedTextColor.YELLOW;
97143
}
98144

99-
return "no";
145+
return NamedTextColor.RED;
146+
}
147+
148+
private static @NotNull Component line(@NotNull String key, @Nullable Object value) {
149+
return Component.text(key + ": ", NamedTextColor.GRAY).append(Component.text(String.valueOf(value), NamedTextColor.GOLD));
100150
}
101151

102152
private static @NotNull String formatInstant(@Nullable Instant instant) {
@@ -107,19 +157,35 @@ private static boolean isLoaded(@NotNull DatabaseStatus status) {
107157
return TIME_FORMAT.format(instant);
108158
}
109159

110-
private static @NotNull String formatFailure(@Nullable String message) {
111-
if (message == null || message.isBlank()) {
160+
private static @NotNull String value(@Nullable Object value) {
161+
if (value == null) {
112162
return "none";
113163
}
114164

115-
return message;
165+
String string = String.valueOf(value);
166+
if (string.isBlank()) {
167+
return "none";
168+
}
169+
170+
return string;
116171
}
117172

118-
private static @NotNull String value(@Nullable Object value) {
119-
if (value == null) {
120-
return "none";
173+
private static @Nullable String firstPresent(@Nullable String first, @Nullable String second) {
174+
String normalizedFirst = normalize(first);
175+
176+
if (normalizedFirst != null) {
177+
return normalizedFirst;
121178
}
122179

123-
return String.valueOf(value);
180+
return normalize(second);
124181
}
125-
}
182+
183+
private static @Nullable String normalize(@Nullable String value) {
184+
if (value == null || value.isBlank()) {
185+
return null;
186+
}
187+
188+
return value.trim();
189+
}
190+
191+
}

0 commit comments

Comments
 (0)