diff --git a/README.md b/README.md index 89b3cf4..1c7c095 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,30 @@ Usage of formatting codes in names can be disabled in the config file. Alongside the `/name` command, this mod also supports reading prefixes and suffixes from the LuckPerms mod. +## API + +Other Fabric or NeoForge mods can read names managed by Custom Names through `CustomNameApi`: + +```java +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import xyz.eclipseisoffline.eclipsescustomname.api.CustomNameApi; + +Component prefix = CustomNameApi.getPrefix(player); +Component nickname = CustomNameApi.getNickname(player); +Component displayNickname = CustomNameApi.getDisplayNickname(player); +Component suffix = CustomNameApi.getSuffix(player); +Component fullName = CustomNameApi.getFullName(player); +``` + +`getPrefix`, `getNickname`, and `getSuffix` return `null` when that Custom Names value is not set. `getDisplayNickname` +falls back to the player's vanilla name, and `getFullName` returns the complete display name used by Custom Names. +LuckPerms names can be read with `getLuckPermsPrefix` and `getLuckPermsSuffix`. + +On Fabric, if [Patbox's Placeholder API](https://modrinth.com/mod/placeholder-api) is installed, Custom Names also registers these player placeholders: +`%eclipsescustomname:prefix%`, `%eclipsescustomname:nickname%`, `%eclipsescustomname:suffix%`, +`%eclipsescustomname:luckperms_prefix%`, `%eclipsescustomname:luckperms_suffix%`, and `%eclipsescustomname:full_name%`. + ## Config file The mod's configuration file is present in `{root config directory}/eclipsescustomname.json`. diff --git a/build.gradle.kts b/build.gradle.kts index 6154804..9418b1a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,6 +20,10 @@ multimod { name = "eclipseisoffline" url = uri("https://maven.eclipseisoffline.xyz/releases") } + maven { + name = "Nucleoid" + url = uri("https://maven.nucleoid.xyz") + } } } diff --git a/common/src/main/java/xyz/eclipseisoffline/eclipsescustomname/CustomName.java b/common/src/main/java/xyz/eclipseisoffline/eclipsescustomname/CustomName.java index 64fd7a3..088b9cf 100644 --- a/common/src/main/java/xyz/eclipseisoffline/eclipsescustomname/CustomName.java +++ b/common/src/main/java/xyz/eclipseisoffline/eclipsescustomname/CustomName.java @@ -30,6 +30,7 @@ public void initialize() { LOGGER.info("Reading config"); config = CustomNameConfig.readOrCreate(getConfigDir()); + registerPlatformIntegrations(); registerCommands(CustomNameCommands::register); } @@ -37,6 +38,9 @@ public void initialize() { protected abstract Path getConfigDir(); + protected void registerPlatformIntegrations() { + } + protected abstract void registerCommands(Consumer> registerer); public static CustomNameConfig getConfig() { diff --git a/common/src/main/java/xyz/eclipseisoffline/eclipsescustomname/PlayerNameManager.java b/common/src/main/java/xyz/eclipseisoffline/eclipsescustomname/PlayerNameManager.java index 117a7eb..125149d 100644 --- a/common/src/main/java/xyz/eclipseisoffline/eclipsescustomname/PlayerNameManager.java +++ b/common/src/main/java/xyz/eclipseisoffline/eclipsescustomname/PlayerNameManager.java @@ -115,6 +115,15 @@ public void updatePlayerName(ServerPlayer player, @Nullable Component name, Name markDirty(player); } + public @Nullable Component getCustomPlayerName(ServerPlayer player, NameType nameType) { + return switch (nameType) { + case PREFIX -> playerPrefixes.get(player.getUUID()); + case SUFFIX -> playerSuffixes.get(player.getUUID()); + case NICKNAME -> playerNicknames.get(player.getUUID()); + default -> throw new IllegalArgumentException("Unsupported custom name type " + nameType); + }; + } + public Component getFullPlayerName(ServerPlayer player) { if (!fullPlayerNames.containsKey(player.getUUID())) { updateFullPlayerName(player); diff --git a/common/src/main/java/xyz/eclipseisoffline/eclipsescustomname/api/CustomNameApi.java b/common/src/main/java/xyz/eclipseisoffline/eclipsescustomname/api/CustomNameApi.java new file mode 100644 index 0000000..6cca0f9 --- /dev/null +++ b/common/src/main/java/xyz/eclipseisoffline/eclipsescustomname/api/CustomNameApi.java @@ -0,0 +1,78 @@ +package xyz.eclipseisoffline.eclipsescustomname.api; + +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import org.jspecify.annotations.Nullable; +import xyz.eclipseisoffline.eclipsescustomname.NameType; +import xyz.eclipseisoffline.eclipsescustomname.PlayerNameManager; + +/** + * Public API for reading player names managed by Custom Names. + */ +public final class CustomNameApi { + + private CustomNameApi() { + } + + /** + * Returns the player's Custom Names prefix, or {@code null} when no prefix is set. + */ + public static @Nullable Component getPrefix(ServerPlayer player) { + return getCustomName(player, NameType.PREFIX); + } + + /** + * Returns the player's Custom Names nickname, or {@code null} when no nickname is set. + */ + public static @Nullable Component getNickname(ServerPlayer player) { + return getCustomName(player, NameType.NICKNAME); + } + + /** + * Returns the player's Custom Names nickname, or their vanilla player name when no nickname is set. + */ + public static Component getDisplayNickname(ServerPlayer player) { + Component nickname = getNickname(player); + return nickname == null ? player.getName() : nickname; + } + + /** + * Returns the player's Custom Names suffix, or {@code null} when no suffix is set. + */ + public static @Nullable Component getSuffix(ServerPlayer player) { + return getCustomName(player, NameType.SUFFIX); + } + + /** + * Returns the player's LuckPerms prefix, or {@code null} when LuckPerms is unavailable or no prefix is set. + */ + public static @Nullable Component getLuckPermsPrefix(ServerPlayer player) { + return getName(player, NameType.LUCKPERMS_PREFIX); + } + + /** + * Returns the player's LuckPerms suffix, or {@code null} when LuckPerms is unavailable or no suffix is set. + */ + public static @Nullable Component getLuckPermsSuffix(ServerPlayer player) { + return getName(player, NameType.LUCKPERMS_SUFFIX); + } + + /** + * Returns the player's full display name as used by Custom Names. + */ + public static Component getFullName(ServerPlayer player) { + return manager(player).getFullPlayerName(player); + } + + private static @Nullable Component getCustomName(ServerPlayer player, NameType nameType) { + return manager(player).getCustomPlayerName(player, nameType); + } + + private static @Nullable Component getName(ServerPlayer player, NameType nameType) { + return manager(player).getPlayerName(player, nameType); + } + + private static PlayerNameManager manager(ServerPlayer player) { + return PlayerNameManager.getPlayerNameManager(player.level().getServer()); + } +} diff --git a/common/src/main/java/xyz/eclipseisoffline/eclipsescustomname/api/package-info.java b/common/src/main/java/xyz/eclipseisoffline/eclipsescustomname/api/package-info.java new file mode 100644 index 0000000..4aa5efe --- /dev/null +++ b/common/src/main/java/xyz/eclipseisoffline/eclipsescustomname/api/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package xyz.eclipseisoffline.eclipsescustomname.api; + +import org.jspecify.annotations.NullMarked; diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 83e8f61..8c8460d 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -11,4 +11,8 @@ multimod.modPublishing { } } +dependencies { + compileOnly(libs.placeholder.api) +} + multimod.fabric(project(":common")) diff --git a/fabric/src/main/java/xyz/eclipseisoffline/eclipsescustomname/compat/PlaceholderApiCompat.java b/fabric/src/main/java/xyz/eclipseisoffline/eclipsescustomname/compat/PlaceholderApiCompat.java new file mode 100644 index 0000000..6b9910d --- /dev/null +++ b/fabric/src/main/java/xyz/eclipseisoffline/eclipsescustomname/compat/PlaceholderApiCompat.java @@ -0,0 +1,50 @@ +package xyz.eclipseisoffline.eclipsescustomname.compat; + +import eu.pb4.placeholders.api.PlaceholderResult; +import eu.pb4.placeholders.api.Placeholders; +import eu.pb4.placeholders.api.ServerPlaceholderContext; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import org.jspecify.annotations.Nullable; +import xyz.eclipseisoffline.eclipsescustomname.CustomName; +import xyz.eclipseisoffline.eclipsescustomname.NameType; +import xyz.eclipseisoffline.eclipsescustomname.PlayerNameManager; + +public final class PlaceholderApiCompat { + + private PlaceholderApiCompat() { + } + + public static void register() { + register("prefix", NameType.PREFIX); + register("nickname", NameType.NICKNAME); + register("suffix", NameType.SUFFIX); + register("luckperms_prefix", NameType.LUCKPERMS_PREFIX); + register("luckperms_suffix", NameType.LUCKPERMS_SUFFIX); + Placeholders.registerServer(CustomName.getModdedIdentifier("full_name"), (context, _) -> value(getPlayerName(context))); + } + + private static void register(String id, NameType nameType) { + Placeholders.registerServer(CustomName.getModdedIdentifier(id), (context, _) -> value(getPlayerName(context, nameType))); + } + + private static @Nullable Component getPlayerName(ServerPlaceholderContext context, NameType nameType) { + ServerPlayer player = context.serverPlayer(); + if (player == null) { + return null; + } + return PlayerNameManager.getPlayerNameManager(context.server()).getPlayerName(player, nameType); + } + + private static @Nullable Component getPlayerName(ServerPlaceholderContext context) { + ServerPlayer player = context.serverPlayer(); + if (player == null) { + return null; + } + return PlayerNameManager.getPlayerNameManager(context.server()).getFullPlayerName(player); + } + + private static PlaceholderResult value(@Nullable Component value) { + return PlaceholderResult.value(value == null ? Component.empty() : value); + } +} diff --git a/fabric/src/main/java/xyz/eclipseisoffline/eclipsescustomname/compat/package-info.java b/fabric/src/main/java/xyz/eclipseisoffline/eclipsescustomname/compat/package-info.java new file mode 100644 index 0000000..4922769 --- /dev/null +++ b/fabric/src/main/java/xyz/eclipseisoffline/eclipsescustomname/compat/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package xyz.eclipseisoffline.eclipsescustomname.compat; + +import org.jspecify.annotations.NullMarked; diff --git a/fabric/src/main/java/xyz/eclipseisoffline/eclipsescustomname/fabric/CustomNameFabric.java b/fabric/src/main/java/xyz/eclipseisoffline/eclipsescustomname/fabric/CustomNameFabric.java index ffbac27..34441cd 100644 --- a/fabric/src/main/java/xyz/eclipseisoffline/eclipsescustomname/fabric/CustomNameFabric.java +++ b/fabric/src/main/java/xyz/eclipseisoffline/eclipsescustomname/fabric/CustomNameFabric.java @@ -6,6 +6,7 @@ import net.fabricmc.loader.api.FabricLoader; import net.minecraft.commands.CommandSourceStack; import xyz.eclipseisoffline.eclipsescustomname.CustomName; +import xyz.eclipseisoffline.eclipsescustomname.compat.PlaceholderApiCompat; import java.nio.file.Path; import java.util.function.Consumer; @@ -27,6 +28,17 @@ protected Path getConfigDir() { return FabricLoader.getInstance().getConfigDir(); } + @Override + protected void registerPlatformIntegrations() { + if (FabricLoader.getInstance().isModLoaded("placeholder-api")) { + try { + PlaceholderApiCompat.register(); + } catch (LinkageError exception) { + LOGGER.warn("Failed to register Placeholder API support", exception); + } + } + } + @Override protected void registerCommands(Consumer> registerer) { CommandRegistrationCallback.EVENT.register((dispatcher, _, _) -> registerer.accept(dispatcher)); diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 0147988..015aa4d 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -39,7 +39,8 @@ "commonpermissionsapi": "*" }, "suggests": { - "luckperms": "*" + "luckperms": "*", + "placeholder-api": "*" }, "custom": { "modmenu": { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ff27eea..9130f2e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,6 +9,7 @@ fabric-api = "0.152.1+26.2" neoforge = "26.2.0.0-beta" luckperms = "5.4" +placeholder-api = "3.1.0-beta.1+26.2" commonpermissionsapi = "0.1.3-26.2" filefixutils = "0.1.3-26.1" @@ -18,6 +19,7 @@ minecraft = {group = "com.mojang", name = "minecraft", version.ref = "minecraft" fabric-api = {group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api"} luckperms = {group = "net.luckperms", name = "api", version.ref = "luckperms"} +placeholder-api = {group = "eu.pb4", name = "placeholder-api", version.ref = "placeholder-api"} commonpermissionsapi = {group = "xyz.eclipseisoffline", name = "commonpermissionsapi", version.ref = "commonpermissionsapi"} filefixutils = {group = "xyz.eclipseisoffline", name = "filefixutils", version.ref = "filefixutils"}