Skip to content

Instantly share code, notes, and snippets.

@noobmobile
Last active December 22, 2023 01:08
Show Gist options
  • Save noobmobile/14bd0e83d129cd023f9505a5cddf1117 to your computer and use it in GitHub Desktop.
Save noobmobile/14bd0e83d129cd023f9505a5cddf1117 to your computer and use it in GitHub Desktop.
Classe para facilitar a transformação de ConfigurationSections, em YML, para objetos java. Possui sistema de adapters altamente flexíveis, adapters nativos: ItemStack, String (chat colors) e List
package com.dont.modelo.utils;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import org.apache.commons.codec.binary.Base64;
import org.bukkit.*;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
public class SectionBuilder<T> {
private static final Map<Class<?>, Adapter<?>> CLASS_ADAPTERS = new HashMap<>();
static {
CLASS_ADAPTERS.put(ItemStack.class, new ItemAdapter());
CLASS_ADAPTERS.put(String.class, new StringAdapter());
CLASS_ADAPTERS.put(Material.class, new MaterialAdapter());
CLASS_ADAPTERS.put(Location.class, new LocationAdapter());
CLASS_ADAPTERS.put(StringList.class, new ListAdapter<>(new StringAdapter()));
CLASS_ADAPTERS.put(ItemList.class, new ListAdapter<>(new ItemAdapter()));
CLASS_ADAPTERS.put(Sound.class, new EnumAdapter<>(Sound.class));
CLASS_ADAPTERS.put(EntityType.class, new EnumAdapter<>(EntityType.class));
}
private final ConfigurationSection mainSection;
private final Class<T> clazz;
private final Constructor<T> constructor;
private final Map<String, Class<?>> parameters;
private final Map<String, Adapter<?>> parametersAdapters;
public static <E> SectionBuilder<E> of(Class<E> clazz, ConfigurationSection section) {
return new SectionBuilder<E>(clazz, section);
}
private SectionBuilder(Class<T> clazz, ConfigurationSection mainSection) {
this.mainSection = mainSection;
this.clazz = clazz;
this.constructor = (Constructor<T>) clazz.getConstructors()[0];
this.parameters = new LinkedHashMap<>();
this.parametersAdapters = new HashMap<>();
}
public SectionBuilder<T> parameter(String key, Class<?> valueClass) {
this.parameters.put(key, valueClass);
return this;
}
public SectionBuilder<T> parameter(String key, Class<?> valueClass, Adapter<?> adapter) {
this.parameters.put(key, valueClass);
this.parametersAdapters.put(key, adapter);
return this;
}
public SectionBuilder<T> adapter(Class<?> clazz, Adapter<?> adapter) {
CLASS_ADAPTERS.put(clazz, adapter);
return this;
}
public List<T> build() {
if (constructor.getParameterCount() - 1 != parameters.size()) { // primeiro argumento é sempre a key, e não precisa ser passado pelo método parameter(String, Class)
throw new IllegalArgumentException("Constructor has " + (constructor.getParameterCount() - 1) + " parameters, but it was passed " + parameters.size() + " parameters.");
}
List<T> toReturn = new ArrayList<>();
for (String key : mainSection.getKeys(false)) {
try {
ConfigurationSection section = mainSection.getConfigurationSection(key);
List<Object> constructorParameters = new ArrayList<>();
constructorParameters.add(key);
for (Map.Entry<String, Class<?>> entry : parameters.entrySet()) {
String parameter = entry.getKey();
Class<?> parameterClass = entry.getValue();
Object object = section.get(parameter);
Adapter<?> adapter = parametersAdapters.getOrDefault(parameter, CLASS_ADAPTERS.get(parameterClass));
constructorParameters.add(adapter == null ? object : adapter.supply(object));
}
T instance = constructor.newInstance(constructorParameters.toArray());
toReturn.add(instance);
} catch (Exception e) {
e.printStackTrace();
}
}
return toReturn;
}
public <E> Map<E, T> build(Function<T, E> extractor) {
return build().stream().collect(Collectors.toMap(extractor, Function.identity()));
}
public static interface Adapter<A> {
A supply(Object object);
}
public static class ItemAdapter implements Adapter<ItemStack> {
private final static MaterialAdapter MATERIAL_ADAPTER = new MaterialAdapter();
private final static StringAdapter STRING_ADAPTER = new StringAdapter();
/* O único atributo necessário é o material
Material: STONE
#Texture: http://textures.minecraft.net/texture/fa8887814578ce7c540d7ab8cc6b2a2e22a7492cc86c65a7e839c887b2ed62 # caso tenha essa propriedade, o item automaticamente ira ser uma skull com a textura setada (pode ser um nick também)
Data: 1
Quantidade: 16
Nome: "&5Nome Legal"
Lore:
- "&6Lore legal"
Enchants:
- "DAMAGE_ALL: 10"
Glow: false
*/
@Override
public ItemStack supply(Object object) {
ConfigurationSection section = (ConfigurationSection) object;
String url = !section.isSet("Texture") ? null : section.getString("Texture");
Material material = MATERIAL_ADAPTER.supply(section.get("Material"));
int data = !section.isSet("Data") ? 0 : section.getInt("Data");
int amount = !section.isSet("Quantidade") ? 1 : section.getInt("Quantidade");
String name = !section.isSet("Nome") ? null : STRING_ADAPTER.supply(section.getString("Nome"));
List<String> lore = !section.isSet("Lore") ? null : section.getStringList("Lore").stream().map(STRING_ADAPTER::supply).collect(Collectors.toList());
Map<Enchantment, Integer> enchants = !section.isSet("Enchants") ? null : section.getStringList("Enchants").stream().map(string -> string.split(":")).collect(Collectors.toMap(array -> Enchantment.getByName(array[0]), array -> Integer.parseInt(array[1].trim())));
boolean glow = section.isSet("Glow") && section.getBoolean("Glow");
ItemStack itemStack = url == null ? new ItemStack(material, amount, (byte) data) : getCustomHead(url);
ItemMeta itemMeta = itemStack.getItemMeta();
if (name != null) itemMeta.setDisplayName(name);
if (lore != null) itemMeta.setLore(lore);
if (glow) {
itemMeta.addEnchant(Enchantment.DURABILITY, 1, true);
itemMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
}
if (enchants != null)
enchants.forEach((enchantment, integer) -> itemMeta.addEnchant(enchantment, integer, true));
itemStack.setItemMeta(itemMeta);
return itemStack;
}
private ItemStack getCustomHead(String url) {
ItemStack itemStack = new ItemStack(Material.SKULL_ITEM, 1, (short) 3);
SkullMeta skullMeta = (SkullMeta) itemStack.getItemMeta();
if (url.length() <= 16) {
skullMeta.setOwner(url);
itemStack.setItemMeta(skullMeta);
return itemStack;
}
if (!url.startsWith("http://textures.minecraft.net/texture/")) {
url = "http://textures.minecraft.net/texture/" + url;
}
try {
GameProfile profile = new GameProfile(UUID.nameUUIDFromBytes(url.getBytes()), null);
profile.getProperties().put("textures", new Property("textures", new String(Base64.encodeBase64(String.format("{textures:{SKIN:{url:\"%s\"}}}", url).getBytes()))));
Field profileField = skullMeta.getClass().getDeclaredField("profile");
profileField.setAccessible(true);
profileField.set(skullMeta, profile);
itemStack.setItemMeta(skullMeta);
} catch (Exception e) {
e.printStackTrace();
}
return itemStack;
}
}
private static class StringAdapter implements Adapter<String> {
@Override
public String supply(Object object) {
return ChatColor.translateAlternateColorCodes('&', (String) object);
}
}
private static class MaterialAdapter implements Adapter<Material> {
@Override
public Material supply(Object object) {
String value = object.toString();
return isNumber(value) ? Material.getMaterial(Integer.parseInt(value)) : Material.valueOf(value.toUpperCase());
}
private boolean isNumber(String string) {
try {
int i = Integer.parseInt(string);
return true;
} catch (NumberFormatException e) {
return false;
}
}
}
public static class ListAdapter<A> implements Adapter<List<A>> {
private final Adapter<A> adapter;
public ListAdapter(Adapter<A> adapter) {
this.adapter = adapter;
}
@Override
public List<A> supply(Object object) {
if (object instanceof ConfigurationSection) {
ConfigurationSection section = (ConfigurationSection) object;
return section.getKeys(false).stream().map(section::getConfigurationSection).map(adapter::supply).collect(Collectors.toList());
} else {
List<Object> list = (List<Object>) object;
return list.stream().map(adapter::supply).collect(Collectors.toList());
}
}
}
public static class LocationAdapter implements Adapter<Location> {
@Override
public Location supply(Object object) {
String value = (String) object;
if (!value.contains(";")) return null;
String[] parts = value.split(";");
double x = Double.parseDouble(parts[0]);
double y = Double.parseDouble(parts[1]);
double z = Double.parseDouble(parts[2]);
float yaw = Float.parseFloat(parts[3]);
float pitch = Float.parseFloat(parts[4]);
World w = Bukkit.getServer().getWorld(parts[5]);
return new Location(w, x, y, z, yaw, pitch);
}
}
public static class EnumAdapter<A extends Enum<A>> implements Adapter<A> {
private final Class<A> enumClass;
public EnumAdapter(Class<A> aEnum) {
this.enumClass = aEnum;
}
@Override
public A supply(Object object) {
return Enum.valueOf(enumClass, (String) object);
}
}
public static class ItemList {
}
public static class StringList {
}
}
SectionBuilder.of(Categoria.class, getConfig().getConfigurationSection("Categorias"))
.parameter("Tamanho", int.class)
.parameter("Nome", String.class)
.parameter("Items", CategoriaItem.class, new SectionBuilder.ListAdapter<>(object -> {
ConfigurationSection section = (ConfigurationSection) object;
int slot = section.getInt("Slot");
SectionBuilder.ItemAdapter itemAdapter = new SectionBuilder.ItemAdapter();
ItemStack item = itemAdapter.supply(section);
return new CategoriaItem(section.getName(), slot, item);
}))
.build().forEach(categoria -> Bukkit.broadcastMessage(categoria.toString()));
public class Categoria {
private String key;
private int size;
private String name;
private List<CategoriaItem> categoriaItems;
public Categoria(String key, int size, String name, List<CategoriaItem> categoriaItems) {
this.key = key;
this.size = size;
this.name = name;
this.categoriaItems = categoriaItems;
}
public String getKey() {
return key;
}
public int getSize() {
return size;
}
public String getName() {
return name;
}
public List<CategoriaItem> getCategoriaItems() {
return categoriaItems;
}
@Override
public String toString() {
return "Categoria{" +
"key='" + key + '\'' +
", size=" + size +
", name='" + name + '\'' +
", categoriaItems=" + categoriaItems +
'}';
}
}
public class CategoriaItem {
private String key;
private int slot;
private ItemStack item;
public CategoriaItem(String key, int slot, ItemStack item) {
this.key = key;
this.slot = slot;
this.item = item;
}
public String getKey() {
return key;
}
public int getSlot() {
return slot;
}
public ItemStack getItem() {
return item;
}
@Override
public String toString() {
return "CategoriaItem{" +
"key='" + key + '\'' +
", slot=" + slot +
", item=" + item +
'}';
}
}
Categorias:
c1:
Tamanho: 27
Nome: "&5Legal"
Items:
it1:
Slot: 10
Material: CHEST
Nome: "&5Bau"
it2:
Slot: 11
Material: DIAMOND
Nome: "&5Diamante"
c2:
Tamanho: 36
Nome: "&5Legal +"
Items:
it1:
Slot: 10
Material: IRON_INGOT
Nome: "&4Ferro"
it2:
Slot: 11
Material: GOLD_INGOT
Nome: "&4Ouro"
public void onEnable() {
SectionBuilder.of(Combustivel.class, getConfig().getConfigurationSection("Combustiveis"))
.parameter("Icone", ItemStack.class)
.parameter("Litros", double.class)
.parameter("Infinito", boolean.class)
.parameter("Global", boolean.class)
.parameter("Delay", long.class)
.build().forEach(combustivel -> Bukkit.broadcastMessage(combustivel.toString()));
}
public class Combustivel {
private String id;
private ItemStack icon;
private double fuel;
private boolean infinite, global;
private long delay;
public Combustivel(String id, ItemStack icon, double fuel, boolean infinite, boolean global, long delay) {
this.id = id;
this.icon = icon;
this.fuel = fuel;
this.infinite = infinite;
this.global = global;
this.delay = delay;
}
public long getDelay() {
return delay;
}
public boolean isInfinite() {
return infinite;
}
public boolean isGlobal() {
return global;
}
public String getId() {
return id;
}
public ItemStack getCombustivel(double combustivel) {
if (combustivel <= 0) {
return new ItemStack(Material.AIR);
}
return new ItemComposer(icon.clone()).setLore(icon.getItemMeta().getLore().stream().map(lore -> lore.replace("<qntd>", "" + (combustivel))).collect(Collectors.toList())).toItemStack();
}
public ItemStack getDefaultCombustivel() {
return getCombustivel(fuel);
}
public double getFuel() {
return fuel;
}
@Override
public String toString() {
return "Combustivel{" +
"id='" + id + '\'' +
", icon=" + icon +
", fuel=" + fuel +
", infinite=" + infinite +
", global=" + global +
", delay=" + delay +
'}';
}
}
Combustiveis:
c1:
Infinito: false
Global: false
Delay: 0
Litros: 50
Icone:
Material: SKULL_ITEM
Texture: fa8887814578ce7c540d7ab8cc6b2a2e22a7492cc86c65a7e839c887b2ed62
Nome: "&aCombustível"
Lore:
- "&7Utilize este item para"
- "&7abastecer suas máquinas."
- ""
- "&7 Este combustível possui"
- "&f <qntd>&7 litros!"
- ""
- "&aClique com o combustível em uma máquina."
c2:
Infinito: false
Global: false
Delay: 0
Litros: 100
Icone:
Material: MAGMA_CREAM
Nome: "&aCombustível"
Lore:
- "&7Utilize este item para"
- "&7abastecer suas máquinas."
- ""
- "&7 Este combustível possui"
- "&f <qntd>&7 litros!"
- ""
- "&aClique com o combustível em uma máquina."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment