From 2174ec6124d61cb98c851617adf698a56023da4e Mon Sep 17 00:00:00 2001 From: Tech Date: Sun, 26 Jan 2025 11:57:08 -0500 Subject: [PATCH] cosmetic provider & cleanup --- .../hmccosmetics/api/HMCCosmeticsAPI.java | 28 ++++++- .../cosmetic/CosmeticProvider.java | 74 +++++++++++++++++++ .../hmccosmetics/cosmetic/CosmeticSlot.java | 63 ++++++++-------- .../hmccosmetics/cosmetic/Cosmetics.java | 43 +++++++++-- .../user/CosmeticUserProvider.java | 13 ++-- .../hmccosmetics/user/CosmeticUsers.java | 4 +- 6 files changed, 173 insertions(+), 52 deletions(-) create mode 100644 common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/CosmeticProvider.java diff --git a/common/src/main/java/com/hibiscusmc/hmccosmetics/api/HMCCosmeticsAPI.java b/common/src/main/java/com/hibiscusmc/hmccosmetics/api/HMCCosmeticsAPI.java index f27c8ada..cada36ac 100644 --- a/common/src/main/java/com/hibiscusmc/hmccosmetics/api/HMCCosmeticsAPI.java +++ b/common/src/main/java/com/hibiscusmc/hmccosmetics/api/HMCCosmeticsAPI.java @@ -2,6 +2,7 @@ package com.hibiscusmc.hmccosmetics.api; import com.hibiscusmc.hmccosmetics.HMCCosmeticsPlugin; import com.hibiscusmc.hmccosmetics.cosmetic.Cosmetic; +import com.hibiscusmc.hmccosmetics.cosmetic.CosmeticProvider; import com.hibiscusmc.hmccosmetics.cosmetic.CosmeticSlot; import com.hibiscusmc.hmccosmetics.cosmetic.Cosmetics; import com.hibiscusmc.hmccosmetics.gui.Menu; @@ -12,6 +13,7 @@ import com.hibiscusmc.hmccosmetics.user.CosmeticUsers; import me.lojosho.hibiscuscommons.nms.NMSHandlers; import me.lojosho.shaded.configurate.ConfigurationNode; import org.bukkit.Color; +import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -143,9 +145,10 @@ public final class HMCCosmeticsAPI { * * @param id the id for the cosmetic slot * @return the {@link CosmeticSlot} associated with the given id + * @apiNote this should be done in your {@link JavaPlugin#onLoad()} or it may error. */ - public static @NotNull CosmeticSlot registerCosmeticSlot(@NotNull String id, BiConsumer consumer) { - return CosmeticSlot.register(id, consumer); + public static @NotNull CosmeticSlot registerCosmeticSlot(@NotNull String id) { + return CosmeticSlot.register(id); } /** @@ -153,6 +156,7 @@ public final class HMCCosmeticsAPI { * * @param provider the provider to register * @throws IllegalArgumentException if another plugin has already registered a provider + * @apiNote this should be done in your {@link JavaPlugin#onLoad()} or it may error. */ public static void registerCosmeticUserProvider(@NotNull CosmeticUserProvider provider) { CosmeticUsers.registerProvider(provider); @@ -167,6 +171,26 @@ public final class HMCCosmeticsAPI { return CosmeticUsers.getProvider(); } + /** + * Registers a new cosmetic user provider to use for constructing {@link Cosmetic} instances. + * + * @param provider the provider to register + * @throws IllegalArgumentException if another plugin has already registered a provider + * @apiNote this should be done in your {@link JavaPlugin#onLoad()} or it may error. + */ + public static void registerCosmeticProvider(@NotNull CosmeticProvider provider) { + Cosmetics.registerProvider(provider); + } + + /** + * Retrieves the current {@link CosmeticProvider} that is in use. + * + * @return the current {@link CosmeticProvider} + */ + public static @NotNull CosmeticProvider getCosmeticProvider() { + return Cosmetics.getProvider(); + } + /** * Retrieves the NMS version of the server as recognized by HMCCosmetics. * diff --git a/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/CosmeticProvider.java b/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/CosmeticProvider.java new file mode 100644 index 00000000..dc39bdee --- /dev/null +++ b/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/CosmeticProvider.java @@ -0,0 +1,74 @@ +package com.hibiscusmc.hmccosmetics.cosmetic; + +import com.hibiscusmc.hmccosmetics.HMCCosmeticsPlugin; +import com.hibiscusmc.hmccosmetics.cosmetic.types.*; +import com.hibiscusmc.hmccosmetics.util.MessagesUtil; +import lombok.extern.slf4j.Slf4j; +import me.lojosho.shaded.configurate.ConfigurationNode; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.function.BiFunction; + +/** + * Allow custom implementations of a {@link Cosmetic}. + */ +@Slf4j +public abstract class CosmeticProvider { + protected static final Map> MAPPINGS = Map.ofEntries( + Map.entry(CosmeticSlot.HELMET, CosmeticArmorType::new), + Map.entry(CosmeticSlot.CHESTPLATE, CosmeticArmorType::new), + Map.entry(CosmeticSlot.LEGGINGS, CosmeticArmorType::new), + Map.entry(CosmeticSlot.BOOTS, CosmeticArmorType::new), + Map.entry(CosmeticSlot.OFFHAND, CosmeticArmorType::new), + + Map.entry(CosmeticSlot.MAINHAND, CosmeticMainhandType::new), + + Map.entry(CosmeticSlot.BACKPACK, CosmeticBackpackType::new), + + Map.entry(CosmeticSlot.BALLOON, CosmeticBalloonType::new), + + Map.entry(CosmeticSlot.EMOTE, CosmeticEmoteType::new) + ); + + private static final String EXCEPTION_MESSAGE = "Unknown slot %s provided for mapping, if you registered your own CosmeticSlot please ensure that you've also registered a custom CosmeticProvider! Or if you have already registered a custom CosmeticProvider ensure it is registered in your plugins `onLoad` method instead of `onEnable`!"; + + /** + * Construct the {@link Cosmetic}. + * @param id the cosmetic id + * @param config the configuration node of the cosmetic + * @param slot the occupying slot of the cosmetic + * @return the {@link Cosmetic} + * @throws IllegalArgumentException if the provided cosmetic could not be mapped + */ + public @NotNull Cosmetic createCosmetic(String id, ConfigurationNode config, CosmeticSlot slot) throws IllegalArgumentException { + final var mapper = MAPPINGS.get(slot); + if(mapper == null) { + throw new IllegalArgumentException( + EXCEPTION_MESSAGE.formatted(slot) + ); + } + + return mapper.apply(id, config); + } + + /** + * Represents the plugin that is providing this {@link CosmeticProvider} + * @return the plugin + */ + public abstract Plugin getProviderPlugin(); + + /** + * Default Implementation. + */ + static class Default extends CosmeticProvider { + public static final CosmeticProvider INSTANCE = new Default(); + + @Override + public Plugin getProviderPlugin() { + return HMCCosmeticsPlugin.getInstance(); + } + } +} \ No newline at end of file diff --git a/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/CosmeticSlot.java b/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/CosmeticSlot.java index 33da4539..2b895702 100644 --- a/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/CosmeticSlot.java +++ b/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/CosmeticSlot.java @@ -1,59 +1,59 @@ package com.hibiscusmc.hmccosmetics.cosmetic; import com.hibiscusmc.hmccosmetics.cosmetic.types.*; +import lombok.Getter; +import lombok.ToString; import me.lojosho.shaded.configurate.ConfigurationNode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.function.Consumer; +@Getter +@ToString public class CosmeticSlot { private static final ConcurrentHashMap REGISTRY = new ConcurrentHashMap<>(); - public static final CosmeticSlot HELMET = register("HELMET", CosmeticArmorType::new); - public static final CosmeticSlot CHESTPLATE = register("CHESTPLATE", CosmeticArmorType::new); - public static final CosmeticSlot LEGGINGS = register("LEGGINGS", CosmeticArmorType::new); - public static final CosmeticSlot BOOTS = register("BOOTS", CosmeticArmorType::new); - public static final CosmeticSlot MAINHAND = register("MAINHAND", CosmeticMainhandType::new); - public static final CosmeticSlot OFFHAND = register("OFFHAND", CosmeticArmorType::new); - public static final CosmeticSlot BACKPACK = register("BACKPACK", CosmeticBackpackType::new); - public static final CosmeticSlot BALLOON = register("BALLOON", CosmeticBalloonType::new); - public static final CosmeticSlot EMOTE = register("EMOTE", CosmeticEmoteType::new); + public static final CosmeticSlot HELMET = register("HELMET"); + public static final CosmeticSlot CHESTPLATE = register("CHESTPLATE"); + public static final CosmeticSlot LEGGINGS = register("LEGGINGS"); + public static final CosmeticSlot BOOTS = register("BOOTS"); + public static final CosmeticSlot MAINHAND = register("MAINHAND"); + public static final CosmeticSlot OFFHAND = register("OFFHAND"); + public static final CosmeticSlot BACKPACK = register("BACKPACK"); + public static final CosmeticSlot BALLOON = register("BALLOON"); + public static final CosmeticSlot EMOTE = register("EMOTE"); private final String name; - private final BiConsumer consumer; - private CosmeticSlot(@NotNull String name, @NotNull BiConsumer consumer) { + private CosmeticSlot(@NotNull String name) { this.name = name; - this.consumer = consumer; - } - - /** - * Accepts the given id and configuration node to run the consumer relating to the ConsumerSlot - * @param id The id of the cosmetic - * @param config The configuration node of the cosmetic - */ - public void accept(@NotNull String id, @NotNull ConfigurationNode config) { - consumer.accept(id, config); } /** * Registers a new slot with the given name. If a slot with the given name already exists, it will be returned. * @param name The name of the slot (This will automatically be converted into all UPPERCASE) * @return The slot that was registered or already exists. + * @throws IllegalArgumentException if a cosmetic slot by that name has already been registered */ @NotNull - public static CosmeticSlot register(@NotNull String name, @NotNull BiConsumer consumer) { - name = name.toUpperCase(); - return REGISTRY.computeIfAbsent(name, key -> new CosmeticSlot(key, consumer)); + public static CosmeticSlot register(@NotNull String name) { + final String upperName = name.toUpperCase(); + if(REGISTRY.containsKey(upperName)) { + throw new IllegalArgumentException("A cosmetic slot with name '" + name + "' is already registered."); + } + + final CosmeticSlot slot = new CosmeticSlot(upperName); + REGISTRY.put(upperName, slot); + return slot; } /** - * Returns an unmodifiable map of all the slots that have been registered. * @return An unmodifiable map of all the slots that have been registered. */ @NotNull @@ -68,8 +68,8 @@ public class CosmeticSlot { */ @Nullable public static CosmeticSlot valueOf(@NotNull String name) { - name = name.toUpperCase(); - return REGISTRY.get(name); + final String upperName = name.toUpperCase(); + return REGISTRY.get(upperName); } /** @@ -78,12 +78,7 @@ public class CosmeticSlot { * @return True if the slot exists, false otherwise. */ public static boolean contains(@NotNull String name) { - name = name.toUpperCase(); - return REGISTRY.containsKey(name); - } - - @Override - public String toString() { - return name; + final String upperName = name.toUpperCase(); + return REGISTRY.containsKey(upperName); } } diff --git a/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/Cosmetics.java b/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/Cosmetics.java index ad24b51b..fd63e9ac 100644 --- a/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/Cosmetics.java +++ b/common/src/main/java/com/hibiscusmc/hmccosmetics/cosmetic/Cosmetics.java @@ -2,15 +2,12 @@ package com.hibiscusmc.hmccosmetics.cosmetic; import com.google.common.collect.HashBiMap; import com.hibiscusmc.hmccosmetics.HMCCosmeticsPlugin; -import com.hibiscusmc.hmccosmetics.api.events.CosmeticTypeRegisterEvent; -import com.hibiscusmc.hmccosmetics.config.Settings; -import com.hibiscusmc.hmccosmetics.cosmetic.types.*; import com.hibiscusmc.hmccosmetics.util.MessagesUtil; +import lombok.extern.slf4j.Slf4j; import me.lojosho.shaded.configurate.CommentedConfigurationNode; import me.lojosho.shaded.configurate.ConfigurateException; import me.lojosho.shaded.configurate.ConfigurationNode; import me.lojosho.shaded.configurate.yaml.YamlConfigurationLoader; -import org.apache.commons.lang3.EnumUtils; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -22,10 +19,12 @@ import java.util.Set; import java.util.logging.Level; import java.util.stream.Stream; +@Slf4j public class Cosmetics { - private static final HashBiMap COSMETICS = HashBiMap.create(); + private static CosmeticProvider PROVIDER = CosmeticProvider.Default.INSTANCE; + public static void addCosmetic(Cosmetic cosmetic) { COSMETICS.put(cosmetic.getId(), cosmetic); } @@ -35,7 +34,7 @@ public class Cosmetics { } public static void removeCosmetic(Cosmetic cosmetic) { - COSMETICS.remove(cosmetic); + COSMETICS.remove(cosmetic.getId()); } @Nullable @@ -92,6 +91,31 @@ public class Cosmetics { } } + /** + * Register a custom {@link CosmeticProvider} to provide your own user implementation to + * be used and queried. + * @param provider the provider to register + * @throws IllegalArgumentException if the provider is already registered by another plugin + */ + public static void registerProvider(final CosmeticProvider provider) { + if(PROVIDER != CosmeticProvider.Default.INSTANCE) { + throw new IllegalArgumentException("CosmeticProvider already registered by %s, this conflicts with %s attempting to register their own.".formatted( + PROVIDER.getProviderPlugin().getName(), + provider.getProviderPlugin().getName() + )); + } + + PROVIDER = provider; + } + + /** + * Fetch the current {@link CosmeticProvider} being used. + * @return the current {@link CosmeticProvider} being used + */ + public static CosmeticProvider getProvider() { + return PROVIDER; + } + private static void setupCosmetics(@NotNull CommentedConfigurationNode config) { for (ConfigurationNode cosmeticConfig : config.childrenMap().values()) { String id = cosmeticConfig.key().toString(); @@ -107,7 +131,12 @@ public class Cosmetics { MessagesUtil.sendDebugMessages("Unable to create " + id + " because " + slotNode.getString() + " is not a valid slot!", Level.WARNING); continue; } - cosmeticSlot.accept(id, cosmeticConfig); + + try { + addCosmetic(PROVIDER.createCosmetic(id, cosmeticConfig, cosmeticSlot)); + } catch(Exception ex) { + log.error("Unable to construct cosmetic for {}, skipping processing it.", id, ex); + } } } } \ No newline at end of file diff --git a/common/src/main/java/com/hibiscusmc/hmccosmetics/user/CosmeticUserProvider.java b/common/src/main/java/com/hibiscusmc/hmccosmetics/user/CosmeticUserProvider.java index 1ce06e0c..a941cda7 100644 --- a/common/src/main/java/com/hibiscusmc/hmccosmetics/user/CosmeticUserProvider.java +++ b/common/src/main/java/com/hibiscusmc/hmccosmetics/user/CosmeticUserProvider.java @@ -1,7 +1,6 @@ package com.hibiscusmc.hmccosmetics.user; import com.hibiscusmc.hmccosmetics.HMCCosmeticsPlugin; -import com.hibiscusmc.hmccosmetics.database.UserData; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; @@ -11,27 +10,27 @@ import java.util.UUID; /** * Allow custom implementations of a {@link CosmeticUser}. */ -public interface CosmeticUserProvider { - CosmeticUserProvider DEFAULT = new Default(); - +public abstract class CosmeticUserProvider { /** * Construct the custom {@link CosmeticUser}. * @param playerId the player uuid * @return the {@link CosmeticUser} * @apiNote This method is called during the {@link PlayerJoinEvent}. */ - @NotNull CosmeticUser createCosmeticUser(@NotNull UUID playerId); + public abstract @NotNull CosmeticUser createCosmeticUser(@NotNull UUID playerId); /** * Represents the plugin that is providing this {@link CosmeticUserProvider} * @return the plugin */ - Plugin getProviderPlugin(); + public abstract Plugin getProviderPlugin(); /** * Default implementation. */ - class Default implements CosmeticUserProvider { + static class Default extends CosmeticUserProvider { + public static CosmeticUserProvider INSTANCE = new Default(); + @Override public @NotNull CosmeticUser createCosmeticUser(@NotNull UUID playerId) { return new CosmeticUser(playerId); diff --git a/common/src/main/java/com/hibiscusmc/hmccosmetics/user/CosmeticUsers.java b/common/src/main/java/com/hibiscusmc/hmccosmetics/user/CosmeticUsers.java index 4ab82fc9..0fb8b0c4 100644 --- a/common/src/main/java/com/hibiscusmc/hmccosmetics/user/CosmeticUsers.java +++ b/common/src/main/java/com/hibiscusmc/hmccosmetics/user/CosmeticUsers.java @@ -13,7 +13,7 @@ import java.util.UUID; public class CosmeticUsers { private static final HashBiMap COSMETIC_USERS = HashBiMap.create(); - private static CosmeticUserProvider PROVIDER = CosmeticUserProvider.DEFAULT; + private static CosmeticUserProvider PROVIDER = CosmeticUserProvider.Default.INSTANCE; /** * Adds a user to the Hashmap of stored CosmeticUsers. This will not override an entry if it already exists. If you need to override, delete then add. @@ -80,7 +80,7 @@ public class CosmeticUsers { * @throws IllegalArgumentException if the provider is already registered by another plugin */ public static void registerProvider(final CosmeticUserProvider provider) { - if(PROVIDER != CosmeticUserProvider.DEFAULT) { + if(PROVIDER != CosmeticUserProvider.Default.INSTANCE) { throw new IllegalArgumentException("CosmeticUserProvider already registered by %s, this conflicts with %s attempting to register their own.".formatted( PROVIDER.getProviderPlugin().getName(), provider.getProviderPlugin().getName()