9
0
mirror of https://github.com/HibiscusMC/HMCCosmetics.git synced 2025-12-19 15:09:19 +00:00

Merge pull request #166 from DebitCardz/feat/cosmetic-provider

cosmetic provider & cleanup
This commit is contained in:
LoJoSho
2025-01-26 16:33:48 -06:00
committed by GitHub
6 changed files with 170 additions and 51 deletions

View File

@@ -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<String, ConfigurationNode> 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.
*

View File

@@ -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<CosmeticSlot, BiFunction<String, ConfigurationNode, Cosmetic>> 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();
}
}
}

View File

@@ -1,59 +1,52 @@
package com.hibiscusmc.hmccosmetics.cosmetic;
import com.hibiscusmc.hmccosmetics.cosmetic.types.*;
import me.lojosho.shaded.configurate.ConfigurationNode;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@Getter
public class CosmeticSlot {
private static final ConcurrentHashMap<String, CosmeticSlot> 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<String, ConfigurationNode> consumer;
private CosmeticSlot(@NotNull String name, @NotNull BiConsumer<String, ConfigurationNode> 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<String, ConfigurationNode> 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 +61,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,8 +71,8 @@ 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);
final String upperName = name.toUpperCase();
return REGISTRY.containsKey(upperName);
}
@Override

View File

@@ -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<String, Cosmetic> 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);
}
}
}
}

View File

@@ -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);

View File

@@ -13,7 +13,7 @@ import java.util.UUID;
public class CosmeticUsers {
private static final HashBiMap<UUID, CosmeticUser> 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()