Skip to content

Commit 9ea2d1b

Browse files
vLuckyyyCitralFlo
andauthored
GH-1210 Add placeholder documentation automation. (#1210)
* Add placeholder documentation automation. * Add homes-related placeholders to documentation * Add placeholder documentation for home-related features * Update placeholder documentation to include prefixed names and remove default values * wip * Follow review feedback. standarize messages. --------- Co-authored-by: Michał Wojtas <wojtas.michal90@gmail.com>
1 parent 12c6c9a commit 9ea2d1b

File tree

9 files changed

+327
-68
lines changed

9 files changed

+327
-68
lines changed

eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/AfkPlaceholderSetup.java renamed to eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/placeholder/AfkPlaceholderSetup.java

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
package com.eternalcode.core.feature.afk;
1+
package com.eternalcode.core.feature.afk.placeholder;
22

3+
import com.eternalcode.annotations.scan.placeholder.PlaceholderDocs;
4+
import com.eternalcode.core.feature.afk.Afk;
5+
import com.eternalcode.core.feature.afk.AfkService;
36
import com.eternalcode.core.injector.annotations.Inject;
47
import com.eternalcode.core.injector.annotations.component.Controller;
58
import com.eternalcode.core.placeholder.PlaceholderRegistry;
@@ -28,18 +31,56 @@ class AfkPlaceholderSetup {
2831

2932
@Subscribe(EternalInitializeEvent.class)
3033
void setUpPlaceholders(PlaceholderRegistry placeholderRegistry, AfkService afkService) {
31-
placeholderRegistry.registerPlaceholder(PlaceholderReplacer.of(
34+
placeholderRegistry.registerPlaceholder(this.createAfkPlaceholder(afkService));
35+
placeholderRegistry.registerPlaceholder(this.createAfkFormattedPlaceholder(afkService));
36+
placeholderRegistry.registerPlaceholder(this.createAfkTimePlaceholder(afkService));
37+
placeholderRegistry.registerPlaceholder(this.createAfkPlayerCountPlaceholder(afkService));
38+
}
39+
40+
@PlaceholderDocs(
41+
name = "afk",
42+
description = "Returns true if the player is AFK, false otherwise",
43+
example = "true",
44+
returnType = "boolean",
45+
category = "AFK",
46+
requiresPlayer = true
47+
)
48+
private PlaceholderReplacer createAfkPlaceholder(AfkService afkService) {
49+
return PlaceholderReplacer.of(
3250
"afk",
33-
player -> String.valueOf(afkService.isAfk(player.getUniqueId()))));
34-
placeholderRegistry.registerPlaceholder(PlaceholderReplacer.of(
51+
player -> String.valueOf(afkService.isAfk(player.getUniqueId()))
52+
);
53+
}
54+
55+
@PlaceholderDocs(
56+
name = "afk_formatted",
57+
description = "Returns a formatted AFK status message based on player's language settings",
58+
example = "[AFK]",
59+
returnType = "String",
60+
category = "AFK",
61+
requiresPlayer = true
62+
)
63+
private PlaceholderReplacer createAfkFormattedPlaceholder(AfkService afkService) {
64+
return PlaceholderReplacer.of(
3565
"afk_formatted",
3666
player -> {
3767
Translation messages = this.translationManager.getMessages(player.getUniqueId());
3868
return afkService.isAfk(player.getUniqueId()) ?
3969
messages.afk().afkEnabledPlaceholder() : messages.afk().afkDisabledPlaceholder();
40-
}));
70+
}
71+
);
72+
}
4173

42-
placeholderRegistry.registerPlaceholder(PlaceholderReplacer.of(
74+
@PlaceholderDocs(
75+
name = "afk_time",
76+
description = "Returns the duration the player has been AFK in a formatted string",
77+
example = "5m 30s",
78+
returnType = "String",
79+
category = "AFK",
80+
requiresPlayer = true
81+
)
82+
private PlaceholderReplacer createAfkTimePlaceholder(AfkService afkService) {
83+
return PlaceholderReplacer.of(
4384
"afk_time",
4485
player -> {
4586
Optional<Afk> afkOptional = afkService.getAfk(player.getUniqueId());
@@ -52,16 +93,28 @@ void setUpPlaceholders(PlaceholderRegistry placeholderRegistry, AfkService afkSe
5293
Instant now = Instant.now();
5394
Duration afkDuration = Duration.between(start, now);
5495
return DurationUtil.format(afkDuration, true);
55-
}));
96+
}
97+
);
98+
}
5699

57-
placeholderRegistry.registerPlaceholder(PlaceholderReplacer.of(
100+
@PlaceholderDocs(
101+
name = "afk_playercount",
102+
description = "Returns the total number of AFK players on the server",
103+
example = "3",
104+
returnType = "int",
105+
category = "AFK",
106+
requiresPlayer = false
107+
)
108+
private PlaceholderReplacer createAfkPlayerCountPlaceholder(AfkService afkService) {
109+
return PlaceholderReplacer.of(
58110
"afk_playercount",
59-
player -> {
111+
ignoredPlayer -> {
60112
long afkPlayerCount = this.server.getOnlinePlayers()
61113
.stream()
62114
.filter(onlinePlayer -> afkService.isAfk(onlinePlayer.getUniqueId()))
63115
.count();
64116
return String.valueOf(afkPlayerCount);
65-
}));
117+
}
118+
);
66119
}
67120
}
Lines changed: 78 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.eternalcode.core.feature.home;
22

3+
import com.eternalcode.annotations.scan.placeholder.PlaceholderDocs;
34
import com.eternalcode.core.injector.annotations.Inject;
45
import com.eternalcode.core.injector.annotations.component.Controller;
56
import com.eternalcode.core.placeholder.PlaceholderRegistry;
@@ -28,46 +29,92 @@ class HomePlaceholderSetup {
2829
@Subscribe(EternalInitializeEvent.class)
2930
void setUp(PlaceholderRegistry placeholderRegistry) {
3031
Stream.of(
31-
PlaceholderReplacer.of("homes_owned", (text, targetPlayer) -> this.ownedHomes(targetPlayer)),
32-
PlaceholderReplacer.of("homes_count", (text, targetPlayer) -> this.homesCount(targetPlayer)),
33-
PlaceholderReplacer.of("homes_limit", (text, targetPlayer) -> this.homesLimit(targetPlayer)),
34-
PlaceholderReplacer.of("homes_left", (text, targetPlayer) -> this.homesLeft(targetPlayer))
35-
).forEach(placeholder -> placeholderRegistry.registerPlaceholder(placeholder));
32+
this.createHomesOwnedPlaceholder(),
33+
this.createHomesCountPlaceholder(),
34+
this.createHomesLimitPlaceholder(),
35+
this.createHomesLeftPlaceholder()
36+
).forEach(placeholderRegistry::registerPlaceholder);
3637
}
3738

38-
private String homesLeft(Player targetPlayer) {
39-
int homesLimit = this.homeService.getHomeLimit(targetPlayer);
40-
int amountOfHomes = this.homeService.getAmountOfHomes(targetPlayer.getUniqueId());
39+
@PlaceholderDocs(
40+
name = "homes_owned",
41+
description = "Returns a comma-separated list of all homes owned by the player. If the player has no homes, returns a localized message.",
42+
example = "home1, home2, domek",
43+
returnType = "String",
44+
category = "Home",
45+
requiresPlayer = true
46+
)
47+
private PlaceholderReplacer createHomesOwnedPlaceholder() {
48+
return PlaceholderReplacer.of(
49+
"homes_owned",
50+
(text, targetPlayer) -> {
51+
Collection<Home> homes = this.homeService.getHomes(targetPlayer.getUniqueId());
52+
Translation translation = this.translationManager.getMessages(targetPlayer.getUniqueId());
4153

42-
return homesLeft(homesLimit, amountOfHomes);
43-
}
44-
45-
static String homesLeft(int homesLimit, int amountOfHomes) {
46-
if (homesLimit < -1 || amountOfHomes > homesLimit) {
47-
return "0";
48-
}
54+
if (homes.isEmpty()) {
55+
return translation.home().noHomesOwnedPlaceholder();
56+
}
4957

50-
int result = homesLimit - amountOfHomes;
51-
52-
return String.valueOf(result);
58+
return homes.stream()
59+
.map(Home::getName)
60+
.collect(Collectors.joining(", "));
61+
}
62+
);
5363
}
5464

55-
private String ownedHomes(Player targetPlayer) {
56-
Collection<Home> homes = this.homeService.getHomes(targetPlayer.getUniqueId());
57-
Translation translation = this.translationManager.getMessages(targetPlayer.getUniqueId());
58-
59-
if (homes.isEmpty()) {
60-
return translation.home().noHomesOwnedPlaceholder();
61-
}
62-
63-
return homes.stream().map(Home::getName).collect(Collectors.joining(", "));
65+
@PlaceholderDocs(
66+
name = "homes_count",
67+
description = "Returns the number of homes the player currently owns.",
68+
example = "3",
69+
returnType = "int",
70+
category = "Home",
71+
requiresPlayer = true
72+
)
73+
private PlaceholderReplacer createHomesCountPlaceholder() {
74+
return PlaceholderReplacer.of(
75+
"homes_count",
76+
(text, targetPlayer) ->
77+
String.valueOf(this.homeService.getAmountOfHomes(targetPlayer.getUniqueId()))
78+
);
6479
}
6580

66-
private String homesCount(Player targetPlayer) {
67-
return String.valueOf(this.homeService.getAmountOfHomes(targetPlayer.getUniqueId()));
81+
@PlaceholderDocs(
82+
name = "homes_limit",
83+
description = "Returns the maximum number of homes the player can have.",
84+
example = "5",
85+
returnType = "int",
86+
category = "Home",
87+
requiresPlayer = true
88+
)
89+
private PlaceholderReplacer createHomesLimitPlaceholder() {
90+
return PlaceholderReplacer.of(
91+
"homes_limit",
92+
(text, targetPlayer) ->
93+
String.valueOf(this.homeService.getHomeLimit(targetPlayer))
94+
);
6895
}
6996

70-
private String homesLimit(Player targetPlayer) {
71-
return String.valueOf(this.homeService.getHomeLimit(targetPlayer));
97+
@PlaceholderDocs(
98+
name = "homes_left",
99+
description = "Returns how many more homes the player can create before reaching the limit.",
100+
example = "2",
101+
returnType = "int",
102+
category = "Home",
103+
requiresPlayer = true
104+
)
105+
private PlaceholderReplacer createHomesLeftPlaceholder() {
106+
return PlaceholderReplacer.of(
107+
"homes_left",
108+
(text, targetPlayer) -> {
109+
int homesLimit = this.homeService.getHomeLimit(targetPlayer);
110+
int amountOfHomes = this.homeService.getAmountOfHomes(targetPlayer.getUniqueId());
111+
112+
if (homesLimit < -1 || amountOfHomes > homesLimit) {
113+
return "0";
114+
}
115+
116+
return String.valueOf(homesLimit - amountOfHomes);
117+
}
118+
);
72119
}
73120
}

eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/EternalScanner.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.eternalcode.annotations.scan.reflect.PackageUtil;
55

66
import java.util.ArrayList;
7+
import java.util.Comparator;
78
import java.util.List;
89

910
public class EternalScanner {
@@ -16,10 +17,13 @@ public EternalScanner(ClassLoader classLoader, Package packageToScan) {
1617
this.packageToScan = packageToScan;
1718
}
1819

19-
public <RESULT, RESOLVER extends EternalScanResolver<RESULT>> List<RESULT> scan(RESOLVER resolver) {
20+
public <RESULT, RESOLVER extends EternalScanResolver<RESULT>> List<RESULT> scan(RESOLVER resolver, Comparator<RESULT> sort) {
2021
PackageStack packageStack = PackageUtil.createPackageStack(this.packageToScan, this.classLoader);
2122

22-
return this.scan(packageStack, resolver);
23+
return this.scan(packageStack, resolver).stream()
24+
.sorted(sort)
25+
.distinct()
26+
.toList();
2327
}
2428

2529
private <RESULT, RESOLVER extends EternalScanResolver<RESULT>> List<RESULT> scan(PackageStack packageStack, RESOLVER resolver) {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.eternalcode.annotations.scan.placeholder;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
@Retention(RetentionPolicy.RUNTIME)
9+
@Target({ ElementType.METHOD, ElementType.FIELD })
10+
public @interface PlaceholderDocs {
11+
12+
/**
13+
* The name of the placeholder (without % symbols)
14+
* Example: "afk" for %afk%
15+
*/
16+
String name();
17+
18+
/**
19+
* Description of what the placeholder does
20+
*/
21+
String description();
22+
23+
/**
24+
* Example output of the placeholder
25+
*/
26+
String example();
27+
28+
/**
29+
* Return type of the placeholder
30+
*/
31+
String returnType();
32+
33+
/**
34+
* Category for grouping placeholders
35+
*/
36+
String category() default "General";
37+
38+
/**
39+
* Whether the placeholder requires a player context
40+
*/
41+
boolean requiresPlayer();
42+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.eternalcode.annotations.scan.placeholder;
2+
3+
public record PlaceholderResult(
4+
String name,
5+
String description,
6+
String example,
7+
String returnType,
8+
String category,
9+
boolean requiresPlayer
10+
) {
11+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.eternalcode.annotations.scan.placeholder;
2+
3+
import com.eternalcode.annotations.scan.EternalScanRecord;
4+
import com.eternalcode.annotations.scan.SingleAnnotationScanResolver;
5+
6+
public class PlaceholderScanResolver extends SingleAnnotationScanResolver<PlaceholderDocs, PlaceholderResult> {
7+
8+
public PlaceholderScanResolver() {
9+
super(PlaceholderDocs.class);
10+
}
11+
12+
@Override
13+
public PlaceholderResult resolve(EternalScanRecord record, PlaceholderDocs annotation) {
14+
String prefixedName = "%eternalcore_" + annotation.name() + "%";
15+
return new PlaceholderResult(
16+
prefixedName,
17+
annotation.description(),
18+
annotation.example(),
19+
annotation.returnType(),
20+
annotation.category(),
21+
annotation.requiresPlayer()
22+
);
23+
}
24+
}

eternalcore-docs-generate/build.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,17 @@ dependencies {
2222
runtimeOnly("dev.rollczi:liteskullapi:${Versions.LITE_SKULL_API}")
2323
runtimeOnly("commons-io:commons-io:${Versions.APACHE_COMMONS}")
2424
runtimeOnly("dev.triumphteam:triumph-gui:${Versions.TRIUMPH_GUI}")
25+
runtimeOnly("eu.okaeri:okaeri-configs-yaml-snakeyaml:${Versions.OKAERI_CONFIGS}")
26+
runtimeOnly("eu.okaeri:okaeri-configs-serdes-commons:${Versions.OKAERI_CONFIGS}")
2527
runtimeOnly("org.bstats:bstats-bukkit:${Versions.BSTATS}")
2628
runtimeOnly("com.github.ben-manes.caffeine:caffeine:${Versions.CAFFEINE}")
2729
runtimeOnly("com.eternalcode:multification-core:${Versions.MULTIFICATION}")
2830
runtimeOnly("com.eternalcode:eternalcode-commons-bukkit:${Versions.ETERNALCODE_COMMONS}")
2931
runtimeOnly("com.eternalcode:eternalcode-commons-adventure:${Versions.ETERNALCODE_COMMONS}")
32+
runtimeOnly("com.eternalcode:eternalcode-commons-folia:${Versions.ETERNALCODE_COMMONS}")
33+
runtimeOnly("com.eternalcode:eternalcode-commons-updater:${Versions.ETERNALCODE_COMMONS}")
34+
runtimeOnly("com.github.cryptomorin:XSeries:${Versions.XSERIES}")
35+
runtimeOnly("us.dynmap:dynmap-api:${Versions.DYNMAP_API}")
36+
runtimeOnly("us.dynmap:DynmapCoreAPI:${Versions.DYNMAP_API}")
37+
runtimeOnly("fr.skytasul:glowingentities:${Versions.GLOWING_ENTITIES}")
3038
}

0 commit comments

Comments
 (0)