From 458cf717fd0a2d5c4565df9645325f73de2e4f4b Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Wed, 15 Oct 2025 14:46:32 +0200 Subject: [PATCH 1/6] Add placeholder documentation automation. --- .../AfkPlaceholderSetup.java | 71 ++++++++++++++++--- .../scan/placeholder/PlaceholderDocs.java | 42 +++++++++++ .../scan/placeholder/PlaceholderResult.java | 11 +++ .../placeholder/PlaceholderScanResolver.java | 23 ++++++ eternalcore-docs-generate/build.gradle.kts | 8 +++ .../annotations/scan/GenerateDocs.java | 29 +++++++- raw_eternalcore_placeholders.json | 34 +++++++++ 7 files changed, 207 insertions(+), 11 deletions(-) rename eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/{ => placeholder}/AfkPlaceholderSetup.java (50%) create mode 100644 eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderDocs.java create mode 100644 eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderResult.java create mode 100644 eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderScanResolver.java create mode 100644 raw_eternalcore_placeholders.json diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/AfkPlaceholderSetup.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/placeholder/AfkPlaceholderSetup.java similarity index 50% rename from eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/AfkPlaceholderSetup.java rename to eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/placeholder/AfkPlaceholderSetup.java index d27c63541..13ff13253 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/AfkPlaceholderSetup.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/placeholder/AfkPlaceholderSetup.java @@ -1,5 +1,8 @@ -package com.eternalcode.core.feature.afk; +package com.eternalcode.core.feature.afk.placeholder; +import com.eternalcode.annotations.scan.placeholder.PlaceholderDocs; +import com.eternalcode.core.feature.afk.Afk; +import com.eternalcode.core.feature.afk.AfkService; import com.eternalcode.core.injector.annotations.Inject; import com.eternalcode.core.injector.annotations.component.Controller; import com.eternalcode.core.placeholder.PlaceholderRegistry; @@ -28,18 +31,56 @@ class AfkPlaceholderSetup { @Subscribe(EternalInitializeEvent.class) void setUpPlaceholders(PlaceholderRegistry placeholderRegistry, AfkService afkService) { - placeholderRegistry.registerPlaceholder(PlaceholderReplacer.of( + placeholderRegistry.registerPlaceholder(this.createAfkPlaceholder(afkService)); + placeholderRegistry.registerPlaceholder(this.createAfkFormattedPlaceholder(afkService)); + placeholderRegistry.registerPlaceholder(this.createAfkTimePlaceholder(afkService)); + placeholderRegistry.registerPlaceholder(this.createAfkPlayerCountPlaceholder(afkService)); + } + + @PlaceholderDocs( + name = "afk", + description = "Returns true if the player is AFK, false otherwise", + example = "true", + returnType = "boolean", + category = "AFK", + requiresPlayer = true + ) + private PlaceholderReplacer createAfkPlaceholder(AfkService afkService) { + return PlaceholderReplacer.of( "afk", - player -> String.valueOf(afkService.isAfk(player.getUniqueId())))); - placeholderRegistry.registerPlaceholder(PlaceholderReplacer.of( + player -> String.valueOf(afkService.isAfk(player.getUniqueId())) + ); + } + + @PlaceholderDocs( + name = "afk_formatted", + description = "Returns a formatted AFK status message based on player's language settings", + example = "[AFK]", + returnType = "String", + category = "AFK", + requiresPlayer = true + ) + private PlaceholderReplacer createAfkFormattedPlaceholder(AfkService afkService) { + return PlaceholderReplacer.of( "afk_formatted", player -> { Translation messages = this.translationManager.getMessages(player.getUniqueId()); return afkService.isAfk(player.getUniqueId()) ? messages.afk().afkEnabledPlaceholder() : messages.afk().afkDisabledPlaceholder(); - })); + } + ); + } - placeholderRegistry.registerPlaceholder(PlaceholderReplacer.of( + @PlaceholderDocs( + name = "afk_time", + description = "Returns the duration the player has been AFK in a formatted string", + example = "5m 30s", + returnType = "String", + category = "AFK", + requiresPlayer = true + ) + private PlaceholderReplacer createAfkTimePlaceholder(AfkService afkService) { + return PlaceholderReplacer.of( "afk_time", player -> { Optional afkOptional = afkService.getAfk(player.getUniqueId()); @@ -52,9 +93,20 @@ void setUpPlaceholders(PlaceholderRegistry placeholderRegistry, AfkService afkSe Instant now = Instant.now(); Duration afkDuration = Duration.between(start, now); return DurationUtil.format(afkDuration, true); - })); + } + ); + } - placeholderRegistry.registerPlaceholder(PlaceholderReplacer.of( + @PlaceholderDocs( + name = "afk_playercount", + description = "Returns the total number of AFK players on the server", + example = "3", + returnType = "int", + category = "AFK", + requiresPlayer = false + ) + private PlaceholderReplacer createAfkPlayerCountPlaceholder(AfkService afkService) { + return PlaceholderReplacer.of( "afk_playercount", player -> { long afkPlayerCount = this.server.getOnlinePlayers() @@ -62,6 +114,7 @@ void setUpPlaceholders(PlaceholderRegistry placeholderRegistry, AfkService afkSe .filter(onlinePlayer -> afkService.isAfk(onlinePlayer.getUniqueId())) .count(); return String.valueOf(afkPlayerCount); - })); + } + ); } } diff --git a/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderDocs.java b/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderDocs.java new file mode 100644 index 000000000..075d7b224 --- /dev/null +++ b/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderDocs.java @@ -0,0 +1,42 @@ +package com.eternalcode.annotations.scan.placeholder; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD, ElementType.FIELD }) +public @interface PlaceholderDocs { + + /** + * The name of the placeholder (without % symbols) + * Example: "afk" for %afk% + */ + String name(); + + /** + * Description of what the placeholder does + */ + String description(); + + /** + * Example output of the placeholder + */ + String example() default ""; + + /** + * Return type of the placeholder + */ + String returnType() default "String"; + + /** + * Category for grouping placeholders + */ + String category() default "General"; + + /** + * Whether the placeholder requires a player context + */ + boolean requiresPlayer() default true; +} diff --git a/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderResult.java b/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderResult.java new file mode 100644 index 000000000..d8ca68807 --- /dev/null +++ b/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderResult.java @@ -0,0 +1,11 @@ +package com.eternalcode.annotations.scan.placeholder; + +public record PlaceholderResult( + String name, + String description, + String example, + String returnType, + String category, + boolean requiresPlayer +) { +} diff --git a/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderScanResolver.java b/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderScanResolver.java new file mode 100644 index 000000000..f1c86b05d --- /dev/null +++ b/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderScanResolver.java @@ -0,0 +1,23 @@ +package com.eternalcode.annotations.scan.placeholder; + +import com.eternalcode.annotations.scan.EternalScanRecord; +import com.eternalcode.annotations.scan.SingleAnnotationScanResolver; + +public class PlaceholderScanResolver extends SingleAnnotationScanResolver { + + public PlaceholderScanResolver() { + super(PlaceholderDocs.class); + } + + @Override + public PlaceholderResult resolve(EternalScanRecord record, PlaceholderDocs annotation) { + return new PlaceholderResult( + annotation.name(), + annotation.description(), + annotation.example(), + annotation.returnType(), + annotation.category(), + annotation.requiresPlayer() + ); + } +} diff --git a/eternalcore-docs-generate/build.gradle.kts b/eternalcore-docs-generate/build.gradle.kts index 12ae56845..b70478a09 100644 --- a/eternalcore-docs-generate/build.gradle.kts +++ b/eternalcore-docs-generate/build.gradle.kts @@ -24,9 +24,17 @@ dependencies { runtimeOnly("org.panda-lang:panda-utilities:${Versions.PANDA_UTILITIES}") runtimeOnly("commons-io:commons-io:${Versions.APACHE_COMMONS}") runtimeOnly("dev.triumphteam:triumph-gui:${Versions.TRIUMPH_GUI}") + runtimeOnly("eu.okaeri:okaeri-configs-yaml-snakeyaml:${Versions.OKAERI_CONFIGS}") + runtimeOnly("eu.okaeri:okaeri-configs-serdes-commons:${Versions.OKAERI_CONFIGS}") runtimeOnly("org.bstats:bstats-bukkit:${Versions.BSTATS}") runtimeOnly("com.github.ben-manes.caffeine:caffeine:${Versions.CAFFEINE}") runtimeOnly("com.eternalcode:multification-core:${Versions.MULTIFICATION}") runtimeOnly("com.eternalcode:eternalcode-commons-bukkit:${Versions.ETERNALCODE_COMMONS}") runtimeOnly("com.eternalcode:eternalcode-commons-adventure:${Versions.ETERNALCODE_COMMONS}") + runtimeOnly("com.eternalcode:eternalcode-commons-folia:${Versions.ETERNALCODE_COMMONS}") + runtimeOnly("com.eternalcode:eternalcode-commons-updater:${Versions.ETERNALCODE_COMMONS}") + runtimeOnly("com.github.cryptomorin:XSeries:${Versions.XSERIES}") + runtimeOnly("us.dynmap:dynmap-api:${Versions.DYNMAP_API}") + runtimeOnly("us.dynmap:DynmapCoreAPI:${Versions.DYNMAP_API}") + runtimeOnly("fr.skytasul:glowingentities:${Versions.GLOWING_ENTITIES}") } diff --git a/eternalcore-docs-generate/src/main/java/com/eternalcode/annotations/scan/GenerateDocs.java b/eternalcore-docs-generate/src/main/java/com/eternalcode/annotations/scan/GenerateDocs.java index e6ca1ea71..36fe8e419 100644 --- a/eternalcore-docs-generate/src/main/java/com/eternalcode/annotations/scan/GenerateDocs.java +++ b/eternalcore-docs-generate/src/main/java/com/eternalcode/annotations/scan/GenerateDocs.java @@ -4,6 +4,8 @@ import com.eternalcode.annotations.scan.command.CommandScanResolver; import com.eternalcode.annotations.scan.permission.PermissionResult; import com.eternalcode.annotations.scan.permission.PermissionScanResolver; +import com.eternalcode.annotations.scan.placeholder.PlaceholderResult; +import com.eternalcode.annotations.scan.placeholder.PlaceholderScanResolver; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.io.FileWriter; @@ -17,24 +19,38 @@ public static void main(String[] args) throws ClassNotFoundException { Class aClass = Class.forName("com.eternalcode.core.EternalCore"); EternalScanner scanner = new EternalScanner(aClass.getClassLoader(), aClass.getPackage()); + // Scan commands List commandResults = scanner.scan(new CommandScanResolver()) .stream() .sorted(Comparator.comparing(CommandResult::name)) .distinct() .toList(); + // Scan permissions List permissionResults = scanner.scan(new PermissionScanResolver()) .stream() .sorted(Comparator.comparing(PermissionResult::name)) .distinct() .toList(); + // Scan placeholders + List placeholderResults = scanner.scan(new PlaceholderScanResolver()) + .stream() + .sorted(Comparator.comparing(PlaceholderResult::name)) + .distinct() + .toList(); + Gson gson = new GsonBuilder() .setPrettyPrinting() .disableHtmlEscaping() .create(); - DocumentationResult combinedDocs = new DocumentationResult(commandResults, permissionResults); + // Generate combined documentation + DocumentationResult combinedDocs = new DocumentationResult( + commandResults, + permissionResults, + placeholderResults + ); try (FileWriter fileWriter = new FileWriter("raw_eternalcore_documentation.json")) { gson.toJson(combinedDocs, fileWriter); @@ -42,10 +58,19 @@ public static void main(String[] args) throws ClassNotFoundException { catch (IOException exception) { exception.printStackTrace(); } + + // Generate separate placeholder documentation + try (FileWriter fileWriter = new FileWriter("raw_eternalcore_placeholders.json")) { + gson.toJson(placeholderResults, fileWriter); + } + catch (IOException exception) { + exception.printStackTrace(); + } } public record DocumentationResult( List commands, - List permissions + List permissions, + List placeholders ) {} } diff --git a/raw_eternalcore_placeholders.json b/raw_eternalcore_placeholders.json new file mode 100644 index 000000000..6e829f791 --- /dev/null +++ b/raw_eternalcore_placeholders.json @@ -0,0 +1,34 @@ +[ + { + "name": "afk", + "description": "Returns true if the player is AFK, false otherwise", + "example": "true", + "returnType": "boolean", + "category": "AFK", + "requiresPlayer": true + }, + { + "name": "afk_formatted", + "description": "Returns a formatted AFK status message based on player's language settings", + "example": "[AFK]", + "returnType": "String", + "category": "AFK", + "requiresPlayer": true + }, + { + "name": "afk_playercount", + "description": "Returns the total number of AFK players on the server", + "example": "3", + "returnType": "int", + "category": "AFK", + "requiresPlayer": false + }, + { + "name": "afk_time", + "description": "Returns the duration the player has been AFK in a formatted string", + "example": "5m 30s", + "returnType": "String", + "category": "AFK", + "requiresPlayer": true + } +] \ No newline at end of file From 0f5dc674737939de33825d7e7903b768756ebbe5 Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Wed, 15 Oct 2025 14:58:41 +0200 Subject: [PATCH 2/6] Add homes-related placeholders to documentation --- raw_eternalcore_placeholders.json | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/raw_eternalcore_placeholders.json b/raw_eternalcore_placeholders.json index 6e829f791..8f305d404 100644 --- a/raw_eternalcore_placeholders.json +++ b/raw_eternalcore_placeholders.json @@ -30,5 +30,37 @@ "returnType": "String", "category": "AFK", "requiresPlayer": true + }, + { + "name": "homes_count", + "description": "Returns the number of homes the player currently owns.", + "example": "3", + "returnType": "int", + "category": "Home", + "requiresPlayer": true + }, + { + "name": "homes_left", + "description": "Returns how many more homes the player can create before reaching the limit.", + "example": "2", + "returnType": "int", + "category": "Home", + "requiresPlayer": true + }, + { + "name": "homes_limit", + "description": "Returns the maximum number of homes the player can have.", + "example": "5", + "returnType": "int", + "category": "Home", + "requiresPlayer": true + }, + { + "name": "homes_owned", + "description": "Returns a comma-separated list of all homes owned by the player. If the player has no homes, returns a localized message.", + "example": "home1, home2, domek", + "returnType": "String", + "category": "Home", + "requiresPlayer": true } ] \ No newline at end of file From c0022300dc90fae8e0c08988cbbe495531bb0020 Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Wed, 15 Oct 2025 14:58:53 +0200 Subject: [PATCH 3/6] Add placeholder documentation for home-related features --- .../feature/home/HomePlaceholderSetup.java | 109 +++++++++++++----- 1 file changed, 78 insertions(+), 31 deletions(-) diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/home/HomePlaceholderSetup.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/home/HomePlaceholderSetup.java index f4d695763..1a40d4182 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/home/HomePlaceholderSetup.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/home/HomePlaceholderSetup.java @@ -1,5 +1,6 @@ package com.eternalcode.core.feature.home; +import com.eternalcode.annotations.scan.placeholder.PlaceholderDocs; import com.eternalcode.core.injector.annotations.Inject; import com.eternalcode.core.injector.annotations.component.Controller; import com.eternalcode.core.placeholder.PlaceholderRegistry; @@ -28,46 +29,92 @@ class HomePlaceholderSetup { @Subscribe(EternalInitializeEvent.class) void setUp(PlaceholderRegistry placeholderRegistry) { Stream.of( - PlaceholderReplacer.of("homes_owned", (text, targetPlayer) -> this.ownedHomes(targetPlayer)), - PlaceholderReplacer.of("homes_count", (text, targetPlayer) -> this.homesCount(targetPlayer)), - PlaceholderReplacer.of("homes_limit", (text, targetPlayer) -> this.homesLimit(targetPlayer)), - PlaceholderReplacer.of("homes_left", (text, targetPlayer) -> this.homesLeft(targetPlayer)) - ).forEach(placeholder -> placeholderRegistry.registerPlaceholder(placeholder)); + this.createHomesOwnedPlaceholder(), + this.createHomesCountPlaceholder(), + this.createHomesLimitPlaceholder(), + this.createHomesLeftPlaceholder() + ).forEach(placeholderRegistry::registerPlaceholder); } - private String homesLeft(Player targetPlayer) { - int homesLimit = this.homeService.getHomeLimit(targetPlayer); - int amountOfHomes = this.homeService.getAmountOfHomes(targetPlayer.getUniqueId()); + @PlaceholderDocs( + name = "homes_owned", + description = "Returns a comma-separated list of all homes owned by the player. If the player has no homes, returns a localized message.", + example = "home1, home2, domek", + returnType = "String", + category = "Home", + requiresPlayer = true + ) + private PlaceholderReplacer createHomesOwnedPlaceholder() { + return PlaceholderReplacer.of( + "homes_owned", + (text, targetPlayer) -> { + Collection homes = this.homeService.getHomes(targetPlayer.getUniqueId()); + Translation translation = this.translationManager.getMessages(targetPlayer.getUniqueId()); - return homesLeft(homesLimit, amountOfHomes); - } - - static String homesLeft(int homesLimit, int amountOfHomes) { - if (homesLimit < -1 || amountOfHomes > homesLimit) { - return "0"; - } + if (homes.isEmpty()) { + return translation.home().noHomesOwnedPlaceholder(); + } - int result = homesLimit - amountOfHomes; - - return String.valueOf(result); + return homes.stream() + .map(Home::getName) + .collect(Collectors.joining(", ")); + } + ); } - private String ownedHomes(Player targetPlayer) { - Collection homes = this.homeService.getHomes(targetPlayer.getUniqueId()); - Translation translation = this.translationManager.getMessages(targetPlayer.getUniqueId()); - - if (homes.isEmpty()) { - return translation.home().noHomesOwnedPlaceholder(); - } - - return homes.stream().map(Home::getName).collect(Collectors.joining(", ")); + @PlaceholderDocs( + name = "homes_count", + description = "Returns the number of homes the player currently owns.", + example = "3", + returnType = "int", + category = "Home", + requiresPlayer = true + ) + private PlaceholderReplacer createHomesCountPlaceholder() { + return PlaceholderReplacer.of( + "homes_count", + (text, targetPlayer) -> + String.valueOf(this.homeService.getAmountOfHomes(targetPlayer.getUniqueId())) + ); } - private String homesCount(Player targetPlayer) { - return String.valueOf(this.homeService.getAmountOfHomes(targetPlayer.getUniqueId())); + @PlaceholderDocs( + name = "homes_limit", + description = "Returns the maximum number of homes the player can have.", + example = "5", + returnType = "int", + category = "Home", + requiresPlayer = true + ) + private PlaceholderReplacer createHomesLimitPlaceholder() { + return PlaceholderReplacer.of( + "homes_limit", + (text, targetPlayer) -> + String.valueOf(this.homeService.getHomeLimit(targetPlayer)) + ); } - private String homesLimit(Player targetPlayer) { - return String.valueOf(this.homeService.getHomeLimit(targetPlayer)); + @PlaceholderDocs( + name = "homes_left", + description = "Returns how many more homes the player can create before reaching the limit.", + example = "2", + returnType = "int", + category = "Home", + requiresPlayer = true + ) + private PlaceholderReplacer createHomesLeftPlaceholder() { + return PlaceholderReplacer.of( + "homes_left", + (text, targetPlayer) -> { + int homesLimit = this.homeService.getHomeLimit(targetPlayer); + int amountOfHomes = this.homeService.getAmountOfHomes(targetPlayer.getUniqueId()); + + if (homesLimit < -1 || amountOfHomes > homesLimit) { + return "0"; + } + + return String.valueOf(homesLimit - amountOfHomes); + } + ); } } From 8ed84bd56b30eb52ac26dc1dd61a0c91bf7c693b Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Wed, 15 Oct 2025 15:08:11 +0200 Subject: [PATCH 4/6] Update placeholder documentation to include prefixed names and remove default values --- .../scan/placeholder/PlaceholderDocs.java | 6 +++--- .../placeholder/PlaceholderScanResolver.java | 3 ++- raw_eternalcore_placeholders.json | 16 ++++++++-------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderDocs.java b/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderDocs.java index 075d7b224..fad6c73b7 100644 --- a/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderDocs.java +++ b/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderDocs.java @@ -23,12 +23,12 @@ /** * Example output of the placeholder */ - String example() default ""; + String example(); /** * Return type of the placeholder */ - String returnType() default "String"; + String returnType(); /** * Category for grouping placeholders @@ -38,5 +38,5 @@ /** * Whether the placeholder requires a player context */ - boolean requiresPlayer() default true; + boolean requiresPlayer(); } diff --git a/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderScanResolver.java b/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderScanResolver.java index f1c86b05d..c5b441aeb 100644 --- a/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderScanResolver.java +++ b/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/placeholder/PlaceholderScanResolver.java @@ -11,8 +11,9 @@ public PlaceholderScanResolver() { @Override public PlaceholderResult resolve(EternalScanRecord record, PlaceholderDocs annotation) { + String prefixedName = "%eternalcore_" + annotation.name() + "%"; return new PlaceholderResult( - annotation.name(), + prefixedName, annotation.description(), annotation.example(), annotation.returnType(), diff --git a/raw_eternalcore_placeholders.json b/raw_eternalcore_placeholders.json index 8f305d404..0fe107af9 100644 --- a/raw_eternalcore_placeholders.json +++ b/raw_eternalcore_placeholders.json @@ -1,6 +1,6 @@ [ { - "name": "afk", + "name": "%eternalcore_afk%", "description": "Returns true if the player is AFK, false otherwise", "example": "true", "returnType": "boolean", @@ -8,7 +8,7 @@ "requiresPlayer": true }, { - "name": "afk_formatted", + "name": "%eternalcore_afk_formatted%", "description": "Returns a formatted AFK status message based on player's language settings", "example": "[AFK]", "returnType": "String", @@ -16,7 +16,7 @@ "requiresPlayer": true }, { - "name": "afk_playercount", + "name": "%eternalcore_afk_playercount%", "description": "Returns the total number of AFK players on the server", "example": "3", "returnType": "int", @@ -24,7 +24,7 @@ "requiresPlayer": false }, { - "name": "afk_time", + "name": "%eternalcore_afk_time%", "description": "Returns the duration the player has been AFK in a formatted string", "example": "5m 30s", "returnType": "String", @@ -32,7 +32,7 @@ "requiresPlayer": true }, { - "name": "homes_count", + "name": "%eternalcore_homes_count%", "description": "Returns the number of homes the player currently owns.", "example": "3", "returnType": "int", @@ -40,7 +40,7 @@ "requiresPlayer": true }, { - "name": "homes_left", + "name": "%eternalcore_homes_left%", "description": "Returns how many more homes the player can create before reaching the limit.", "example": "2", "returnType": "int", @@ -48,7 +48,7 @@ "requiresPlayer": true }, { - "name": "homes_limit", + "name": "%eternalcore_homes_limit%", "description": "Returns the maximum number of homes the player can have.", "example": "5", "returnType": "int", @@ -56,7 +56,7 @@ "requiresPlayer": true }, { - "name": "homes_owned", + "name": "%eternalcore_homes_owned%", "description": "Returns a comma-separated list of all homes owned by the player. If the player has no homes, returns a localized message.", "example": "home1, home2, domek", "returnType": "String", From 03821b5395a01f930150011ebdd26b45a1ddb2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Wojtas?= Date: Fri, 17 Oct 2025 17:33:22 +0200 Subject: [PATCH 5/6] wip --- .../annotations/scan/EternalScanner.java | 8 ++- .../annotations/scan/GenerateDocs.java | 60 +++++-------------- 2 files changed, 22 insertions(+), 46 deletions(-) diff --git a/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/EternalScanner.java b/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/EternalScanner.java index ed0458d36..43be81e01 100644 --- a/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/EternalScanner.java +++ b/eternalcore-docs-api/src/main/java/com/eternalcode/annotations/scan/EternalScanner.java @@ -4,6 +4,7 @@ import com.eternalcode.annotations.scan.reflect.PackageUtil; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; public class EternalScanner { @@ -16,10 +17,13 @@ public EternalScanner(ClassLoader classLoader, Package packageToScan) { this.packageToScan = packageToScan; } - public > List scan(RESOLVER resolver) { + public > List scan(RESOLVER resolver, Comparator sort) { PackageStack packageStack = PackageUtil.createPackageStack(this.packageToScan, this.classLoader); - return this.scan(packageStack, resolver); + return this.scan(packageStack, resolver).stream() + .sorted(sort) + .distinct() + .toList(); } private > List scan(PackageStack packageStack, RESOLVER resolver) { diff --git a/eternalcore-docs-generate/src/main/java/com/eternalcode/annotations/scan/GenerateDocs.java b/eternalcore-docs-generate/src/main/java/com/eternalcode/annotations/scan/GenerateDocs.java index 36fe8e419..6e9453e2a 100644 --- a/eternalcore-docs-generate/src/main/java/com/eternalcode/annotations/scan/GenerateDocs.java +++ b/eternalcore-docs-generate/src/main/java/com/eternalcode/annotations/scan/GenerateDocs.java @@ -10,58 +10,31 @@ import com.google.gson.GsonBuilder; import java.io.FileWriter; import java.io.IOException; -import java.util.Comparator; import java.util.List; +import static java.util.Comparator.comparing; public class GenerateDocs { + private static final Gson GSON = new GsonBuilder() + .setPrettyPrinting() + .disableHtmlEscaping() + .create(); + public static void main(String[] args) throws ClassNotFoundException { Class aClass = Class.forName("com.eternalcode.core.EternalCore"); EternalScanner scanner = new EternalScanner(aClass.getClassLoader(), aClass.getPackage()); - // Scan commands - List commandResults = scanner.scan(new CommandScanResolver()) - .stream() - .sorted(Comparator.comparing(CommandResult::name)) - .distinct() - .toList(); - - // Scan permissions - List permissionResults = scanner.scan(new PermissionScanResolver()) - .stream() - .sorted(Comparator.comparing(PermissionResult::name)) - .distinct() - .toList(); - - // Scan placeholders - List placeholderResults = scanner.scan(new PlaceholderScanResolver()) - .stream() - .sorted(Comparator.comparing(PlaceholderResult::name)) - .distinct() - .toList(); - - Gson gson = new GsonBuilder() - .setPrettyPrinting() - .disableHtmlEscaping() - .create(); - - // Generate combined documentation - DocumentationResult combinedDocs = new DocumentationResult( - commandResults, - permissionResults, - placeholderResults - ); + List commandResults = scanner.scan(new CommandScanResolver(), comparing(CommandResult::name)); + List permissionResults = scanner.scan(new PermissionScanResolver(), comparing(PermissionResult::name)); + List placeholderResults = scanner.scan(new PlaceholderScanResolver(), comparing(PlaceholderResult::name)); - try (FileWriter fileWriter = new FileWriter("raw_eternalcore_documentation.json")) { - gson.toJson(combinedDocs, fileWriter); - } - catch (IOException exception) { - exception.printStackTrace(); - } + generateResult("raw_eternalcore_documentation.json", new DocumentationResult(commandResults, permissionResults)); + generateResult("raw_eternalcore_placeholders.json", placeholderResults); + } - // Generate separate placeholder documentation - try (FileWriter fileWriter = new FileWriter("raw_eternalcore_placeholders.json")) { - gson.toJson(placeholderResults, fileWriter); + private static void generateResult(String fileName, Object objectToRender) { + try (FileWriter fileWriter = new FileWriter(fileName)) { + GSON.toJson(objectToRender, fileWriter); } catch (IOException exception) { exception.printStackTrace(); @@ -70,7 +43,6 @@ public static void main(String[] args) throws ClassNotFoundException { public record DocumentationResult( List commands, - List permissions, - List placeholders + List permissions ) {} } From c4424f88e1cb2766cc43b7c52514ef9662fcc0c1 Mon Sep 17 00:00:00 2001 From: Martin Sulikowski Date: Sun, 19 Oct 2025 00:55:55 +0200 Subject: [PATCH 6/6] Follow review feedback. standarize messages. --- .../afk/placeholder/AfkPlaceholderSetup.java | 2 +- .../annotations/scan/GenerateDocs.java | 39 +++++++++++-------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/placeholder/AfkPlaceholderSetup.java b/eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/placeholder/AfkPlaceholderSetup.java index 13ff13253..719ea7612 100644 --- a/eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/placeholder/AfkPlaceholderSetup.java +++ b/eternalcore-core/src/main/java/com/eternalcode/core/feature/afk/placeholder/AfkPlaceholderSetup.java @@ -108,7 +108,7 @@ private PlaceholderReplacer createAfkTimePlaceholder(AfkService afkService) { private PlaceholderReplacer createAfkPlayerCountPlaceholder(AfkService afkService) { return PlaceholderReplacer.of( "afk_playercount", - player -> { + ignoredPlayer -> { long afkPlayerCount = this.server.getOnlinePlayers() .stream() .filter(onlinePlayer -> afkService.isAfk(onlinePlayer.getUniqueId())) diff --git a/eternalcore-docs-generate/src/main/java/com/eternalcode/annotations/scan/GenerateDocs.java b/eternalcore-docs-generate/src/main/java/com/eternalcode/annotations/scan/GenerateDocs.java index 6e9453e2a..5b07bc8a1 100644 --- a/eternalcore-docs-generate/src/main/java/com/eternalcode/annotations/scan/GenerateDocs.java +++ b/eternalcore-docs-generate/src/main/java/com/eternalcode/annotations/scan/GenerateDocs.java @@ -8,39 +8,46 @@ import com.eternalcode.annotations.scan.placeholder.PlaceholderScanResolver; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import java.io.FileWriter; + import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; + import static java.util.Comparator.comparing; public class GenerateDocs { + private static final String TARGET_CLASS = "com.eternalcode.core.EternalCore"; + private static final String DOCS_FILE = "raw_eternalcore_documentation.json"; + private static final String PLACEHOLDERS_FILE = "raw_eternalcore_placeholders.json"; + private static final Gson GSON = new GsonBuilder() .setPrettyPrinting() .disableHtmlEscaping() .create(); - public static void main(String[] args) throws ClassNotFoundException { - Class aClass = Class.forName("com.eternalcode.core.EternalCore"); - EternalScanner scanner = new EternalScanner(aClass.getClassLoader(), aClass.getPackage()); + public static void main(String[] args) { + try { + Class targetClass = Class.forName(TARGET_CLASS); + EternalScanner scanner = new EternalScanner(targetClass.getClassLoader(), targetClass.getPackage()); - List commandResults = scanner.scan(new CommandScanResolver(), comparing(CommandResult::name)); - List permissionResults = scanner.scan(new PermissionScanResolver(), comparing(PermissionResult::name)); - List placeholderResults = scanner.scan(new PlaceholderScanResolver(), comparing(PlaceholderResult::name)); + List commands = scanner.scan(new CommandScanResolver(), comparing(CommandResult::name)); + List permissions = scanner.scan(new PermissionScanResolver(), comparing(PermissionResult::name)); + List placeholders = scanner.scan(new PlaceholderScanResolver(), comparing(PlaceholderResult::name)); - generateResult("raw_eternalcore_documentation.json", new DocumentationResult(commandResults, permissionResults)); - generateResult("raw_eternalcore_placeholders.json", placeholderResults); - } - - private static void generateResult(String fileName, Object objectToRender) { - try (FileWriter fileWriter = new FileWriter(fileName)) { - GSON.toJson(objectToRender, fileWriter); + writeJson(DOCS_FILE, new DocumentationResult(commands, permissions)); + writeJson(PLACEHOLDERS_FILE, placeholders); } - catch (IOException exception) { - exception.printStackTrace(); + catch (Exception exception) { + throw new RuntimeException("Failed to generate documentation", exception); } } + private static void writeJson(String fileName, Object data) throws IOException { + Files.writeString(Path.of(fileName), GSON.toJson(data)); + } + public record DocumentationResult( List commands, List permissions