package mineplex.game.clans.items; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import mineplex.core.MiniPlugin; import mineplex.core.account.CoreClientManager; import mineplex.core.common.util.UtilServer; import mineplex.core.packethandler.IPacketHandler; import mineplex.core.packethandler.PacketHandler; import mineplex.core.packethandler.PacketInfo; import mineplex.core.portal.TransferHandler; import mineplex.core.portal.Commands.SendCommand; import mineplex.core.portal.Commands.ServerCommand; import mineplex.game.clans.items.attributes.ItemAttribute; import mineplex.game.clans.items.attributes.weapon.*; import mineplex.game.clans.items.attributes.armor.*; import mineplex.game.clans.items.commands.GearCommand; import mineplex.game.clans.items.economy.GoldToken; import mineplex.game.clans.items.generation.Weight; import mineplex.game.clans.items.generation.WeightSet; import mineplex.game.clans.items.legendaries.*; import mineplex.game.clans.items.smelting.SmeltingListener; import mineplex.serverdata.Region; import mineplex.serverdata.Utility; import mineplex.serverdata.commands.ServerCommandManager; import mineplex.serverdata.commands.TransferCommand; import mineplex.serverdata.serialization.RuntimeTypeAdapterFactory; import mineplex.serverdata.servers.ServerManager; import net.minecraft.server.v1_7_R4.Packet; import net.minecraft.server.v1_7_R4.PacketPlayOutSetSlot; import net.minecraft.util.com.google.common.collect.Sets; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.craftbukkit.v1_7_R4.inventory.CraftItemStack; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.plugin.java.JavaPlugin; import com.google.gson.Gson; import com.google.gson.GsonBuilder; /** * Manages creation and retrieval of associated {@link PlayerGear}s with online players, as well * as offering methods for parsing and handling {@link CustomItem}s. * @author MrTwiggy * */ public class GearManager extends MiniPlugin implements IPacketHandler, Runnable { private static final String ITEM_SERIALIZATION_TAG = "-JSON-"; private static Gson _gson; private static GearManager _instance; // Singleton instance private Map _playerGears; // Mapping of player names (key) to cached gear set (value). private WeightSet _attributeWeights; // Weightings for randomly selecting number of attributes (1, 2, 3) private WeightSet _typeWeights; // Weightings for randomly selecting item type (legendary/weapon/armour/bow) // Legendary generation private WeightSet> _legendaryWeights; // Weapon generation private WeightSet _weaponTypes; // Armour generation private WeightSet _armourTypes; // Attribute generation private WeightSet> _weaponAttributes; private WeightSet> _armourAttributes; private WeightSet> _bowAttributes; public GearManager(JavaPlugin plugin, PacketHandler packetHandler) { super("CustomGear", plugin); _instance = this; _playerGears = new HashMap(); // TODO: Introduce configurable non-hardcoded values for generation weights? _attributeWeights = new WeightSet(new Weight(3, 3), new Weight(20, 2), new Weight(77, 1)); _typeWeights = new WeightSet(new Weight(10, ItemType.LEGENDARY), new Weight(45, ItemType.ARMOUR), new Weight(22, ItemType.WEAPON), new Weight(0, ItemType.BOW)); // Weapon-based attributes _weaponAttributes = new WeightSet>(FrostedAttribute.class, SharpAttribute.class, JaggedAttribute.class, HasteAttribute.class, FlamingAttribute.class, ConqueringAttribute.class); // Armour-based attributes _armourAttributes = new WeightSet>(SlantedAttribute.class, ReinforcedAttribute.class, ProtectionAttribute.class, PaddedAttribute.class, LavaAttribute.class); // Bow-based attributes //_bowAttributes = new WeightSet>(); // TODO: Add in bow-attributes // Weapon material types _weaponTypes = new WeightSet(Material.DIAMOND_SWORD, Material.GOLD_SWORD, Material.IRON_SWORD, Material.STONE_SWORD, Material.DIAMOND_AXE, Material.GOLD_AXE, Material.IRON_AXE, Material.STONE_AXE); // Armour material types _armourTypes = new WeightSet(Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS, Material.IRON_HELMET, Material.IRON_CHESTPLATE, Material.IRON_LEGGINGS, Material.IRON_BOOTS, Material.GOLD_HELMET, Material.GOLD_CHESTPLATE, Material.GOLD_LEGGINGS, Material.GOLD_BOOTS); // TODO: Initialize list of attributes and types // Initialize various LegendaryItem types _legendaryWeights = new WeightSet>(AlligatorsTooth.class, WindBlade.class, GiantsBroadsword.class, HyperBlade.class, MagneticBlade.class); // TODO: Add rest of legendaries, find better way? // Register listeners UtilServer.getServer().getPluginManager().registerEvents(new ItemListener(), getPlugin()); UtilServer.getServer().getPluginManager().registerEvents(new SmeltingListener(), getPlugin()); // Initialize attribute types factory for JSON handling of polymorphism. RuntimeTypeAdapterFactory attributeFactory = RuntimeTypeAdapterFactory .of(ItemAttribute.class); for (Class attributeType : _armourAttributes.elements()) { attributeFactory.registerSubtype(attributeType); } for (Class attributeType : _weaponAttributes.elements()) { attributeFactory.registerSubtype(attributeType); } // Initialize legendary item type factory for JSON handling of polymorphism. RuntimeTypeAdapterFactory customItemType = RuntimeTypeAdapterFactory .of(CustomItem.class); customItemType.registerSubtype(CustomItem.class); customItemType.registerSubtype(LegendaryItem.class); customItemType.registerSubtype(GoldToken.class); for (Class itemType : _legendaryWeights.elements()) { customItemType.registerSubtype(itemType); } // Build GSON instance off factories for future serialization of items. _gson = new GsonBuilder() .registerTypeAdapterFactory(attributeFactory) .registerTypeAdapterFactory(customItemType) .create(); packetHandler.addPacketHandler(this); plugin.getServer().getScheduler().runTaskTimer(plugin, this, 1l, 1l); } @Override public void addCommands() { addCommand(new GearCommand(this)); } /** * Tick & update internal logic for {@link GearManager}. Called once per tick. */ @Override public void run() { for (PlayerGear gear : _playerGears.values()) { gear.update(); } } /** * @param player - the player whose {@link PlayerGear} set is to be fetched. * @return the cached or newly instantiated {@link PlayerGear} associated with {@code player}. */ public PlayerGear getPlayerGear(Player player) { String playerName = player.getName(); if (!_playerGears.containsKey(playerName)) { PlayerGear gear = new PlayerGear(playerName); _playerGears.put(playerName, gear); } return _playerGears.get(playerName); } public CustomItem generateItem() { int attributeCount = _attributeWeights.generateRandom(); ItemType itemType = _typeWeights.generateRandom(); System.out.println("Generating item of type " + itemType.toString() + " with attribute count of " + attributeCount); CustomItem item = generateItem(itemType); System.out.println("Generated item!"); if (itemType != ItemType.LEGENDARY) // Only non-legendaries have attributes { for (ItemAttribute attribute : generateAttributes(itemType, attributeCount)) { item.addAttribute(attribute); } // Add up to attributeCount attributes } return item; } private CustomItem generateItem(ItemType itemType) { switch(itemType) { case LEGENDARY: // Legendary weapon Class legendaryClass = _legendaryWeights.generateRandom(); System.out.println("Legendary: " + legendaryClass.getName()); return instantiate(legendaryClass); case WEAPON: // Sword or axe return new CustomItem(_weaponTypes.generateRandom()); case ARMOUR: // Helmet, chestplate, leggings, or boots return new CustomItem(_armourTypes.generateRandom()); case BOW: // A bow return new CustomItem(Material.BOW); default: return null;// Never reached, yet required for compilation purposes. } } private Set generateAttributes(ItemType type, int count) { Set attributes = new HashSet(); for (int i = 0; i < count; i++) { switch (type) { case ARMOUR: attributes.add(instantiate(_armourAttributes.generateRandom())); break; case WEAPON: attributes.add(instantiate(_weaponAttributes.generateRandom())); break; default: break; } } return attributes; } public void spawnItem(Location location) { CustomItem item = generateItem(); location.getWorld().dropItem(location, item.toItemStack()); } public static CustomItem parseItem(ItemStack item) { String serialization = getItemSerialization(item); if (serialization != null) { CustomItem customItem = deserialize(serialization); return customItem; } return null; // No serialization found in item's lore, not a custom item! } public static boolean isCustomItem(ItemStack item) { return getItemSerialization(item) != null; } public static String getItemSerialization(CustomItem customItem) { String serialization = serialize(customItem); return ITEM_SERIALIZATION_TAG + serialization; } /** * @param type - the class-type of the object to be instantiated. (must have zero-argument constructor) * @return a newly instantiated instance of {@code type} class-type. Instantied with zero argument constructor. */ private static T instantiate(Class type) { try { return type.newInstance(); } catch (Exception e) { return null; } } private static String getItemSerialization(ItemStack item) { if (item == null || item.getItemMeta() == null || item.getItemMeta().getLore() == null) return null; ItemMeta meta = item.getItemMeta(); for (String lore : meta.getLore()) { if (lore.startsWith(ITEM_SERIALIZATION_TAG)) // Found serialization lore-line { int tagLength = ITEM_SERIALIZATION_TAG.length(); String serialization = lore.substring(tagLength); return serialization; } } return null; // Unable to find any serialized lore lines, hence not a CustomItem. } public static String serialize(CustomItem customItem) { return _gson.toJson(customItem, CustomItem.class); } public static CustomItem deserialize(String serialization) { return _gson.fromJson(serialization, CustomItem.class); } /** * @return singleton instance of {@link GearManager}. */ public static GearManager getInstane() { return _instance; } @Override public void handle(PacketInfo packetInfo) { Packet packet = packetInfo.getPacket(); if (packet instanceof PacketPlayOutSetSlot) { System.out.println("Item slot packet!"); PacketPlayOutSetSlot slotPacket = (PacketPlayOutSetSlot) packet; net.minecraft.server.v1_7_R4.ItemStack original = slotPacket.c; CraftItemStack originalItem = CraftItemStack.asCraftMirror(original); ItemMeta originalMeta = originalItem.getItemMeta(); if (originalMeta == null || originalMeta.getLore() == null) return; // No need to modify item packets with no lore List lore = new ArrayList(); for (String line : originalMeta.getLore()) { if (!line.startsWith(ITEM_SERIALIZATION_TAG)) // Remove serialization lines from out-going lore { lore.add(line); } } net.minecraft.server.v1_7_R4.ItemStack newItem = CraftItemStack.asNMSCopy(originalItem); CraftItemStack newCopy = CraftItemStack.asCraftMirror(newItem); ItemMeta newMeta = newCopy.getItemMeta(); newMeta.setLore(lore); newCopy.setItemMeta(newMeta); slotPacket.c = newItem; //CraftItemStack.setItemMeta(slotPacket.c, meta); // TODO: Modify spigot build so that slotPacket's itemstack lore can be modified // to 'hide' json-encoded lore from players. } } }