Merge pull request #68 in MIN/mineplex from clans_custom_gear to clans-beta

* commit '17fbd42b9cda97a34fae52d73e733c4da24d3128':
  Fix bug where a players inventory was not properly updated after switching to SURVIVAL mode, causing CustomGear items to remain unhidden an JSON encoded.
  Fix offensive skill exploit where players could swap hotbar slots quickly and keep their original offensive spell casting for a large damage and speed advantage (running and attacking). Fix bug edge-case where outgoing slot packets were being missed, causing players CustomGear items to improperly display encoded JSON data instead of a user-friendly item description.
This commit is contained in:
Ty Sayers 2015-10-08 09:36:55 -05:00
commit 739440a2a3
19 changed files with 135 additions and 60 deletions

View File

@ -15,6 +15,7 @@ public class UtilItem
private static final Material[] FOOD_LIST = { Material.APPLE, Material.BAKED_POTATO, Material.BREAD, Material.CARROT, Material.CARROT_ITEM, Material.COOKED_CHICKEN,
Material.COOKED_FISH, Material.GRILLED_PORK, Material.COOKIE, Material.GOLDEN_APPLE, Material.GOLDEN_CARROT, Material.MELON, Material.MUSHROOM_SOUP, Material.POISONOUS_POTATO,
Material.PUMPKIN_PIE, Material.RAW_BEEF, Material.RAW_CHICKEN, Material.RAW_FISH, Material.PORK, Material.ROTTEN_FLESH, Material.SPIDER_EYE, Material.COOKED_BEEF};
private static final Material[] SWORD_LIST = { Material.WOOD_SWORD, Material.IRON_SWORD, Material.GOLD_SWORD, Material.STONE_SWORD, Material.DIAMOND_SWORD };
public static LinkedList<ItemContainer> matchItem(Player caller, String items, boolean inform)
{
@ -166,4 +167,23 @@ public class UtilItem
return false;
}
public static boolean isSword(ItemStack item)
{
return item != null && isSword(item.getType());
}
public static boolean isSword(Material material)
{
for (Material sword : SWORD_LIST)
{
if (sword.equals(material))
{
return true;
}
}
return false;
}
}

View File

@ -4,6 +4,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
@ -29,6 +30,12 @@ import org.bukkit.util.Vector;
public class UtilPlayer
{
private static Random RANDOM = new Random();
// A mapping of player names (Keys) to the system time when they last changed active Hotbar Slot
private static Map<String, Long> _hotbarUpdates = new HashMap<String, Long>();
// The amount of time (in milliseconds) after changin hotbars that you can block
public static final long BLOCKING_HOTBAR_DELAY = 100;
private static boolean hasIntersection(Vector3D p1, Vector3D p2, Vector3D min, Vector3D max)
{
@ -159,6 +166,30 @@ public class UtilPlayer
return hit;
}
/**
* @param player - the player to be checked for blocking status
* @return true, if the {@code player} is blocking and has not recently
* changed hotbar slots (within {@value BLOCKING_HOTBAR_DELAY} milliseconds), false otherwise.
*/
public static boolean isBlocking(Player player)
{
String name = player.getName();
long lastUpdate = _hotbarUpdates.containsKey(name) ? _hotbarUpdates.get(name) : 0;;
long duration = System.currentTimeMillis() - lastUpdate;
return player.isBlocking() && UtilItem.isSword(player.getItemInHand())
&& duration >= BLOCKING_HOTBAR_DELAY;
}
/**
* Mark the {@code player} as having changed hotbar slots.
* @param player - the player to be marked
*/
public static void onHotbarChange(Player player)
{
_hotbarUpdates.put(player.getName(), System.currentTimeMillis());
}
/**
* AviodAllies doesn't work. Leaving as a param as it sounds like something you may want in the future.
*/

View File

@ -15,7 +15,7 @@ public enum ObserverSettings
public static EnumSet<ObserverSettings> getSettings(Rank rank)
{
if (rank.Has(Rank.DEVELOPER))
if (rank.has(Rank.DEVELOPER))
return EnumSet.of(CAN_OPEN_CHESTS);
return EnumSet.noneOf(ObserverSettings.class);

View File

@ -134,7 +134,7 @@ public abstract class ShopBase<PluginType extends MiniPlugin> implements Listene
if (page.matchesInventory(event.getInventory()))
{
_playerPageMap.get(event.getWhoClicked().getName()).playerClicked(event);
page.playerClicked(event);
if (event.getRawSlot() < page.getSize())
{

View File

@ -1,5 +1,5 @@
package mineplex.game.clans.items;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

View File

@ -40,6 +40,7 @@ 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.server.v1_7_R4.PacketPlayOutWindowItems;
import net.minecraft.util.com.google.common.collect.Sets;
import org.bukkit.Bukkit;
@ -432,34 +433,45 @@ public class GearManager extends MiniPlugin implements IPacketHandler, Runnable
Packet packet = packetInfo.getPacket();
if (packet instanceof PacketPlayOutSetSlot)
{
{
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<String> lore = new ArrayList<String>();
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.
slotPacket.c = maskItem(slotPacket.c); // Mask all out-going item packets
}
else if (packet instanceof PacketPlayOutWindowItems)
{
PacketPlayOutWindowItems itemsPacket = (PacketPlayOutWindowItems) packet;
for (int i = 0; i < itemsPacket.b.length; i++)
{
itemsPacket.b[i] = maskItem(itemsPacket.b[i]); // Mask all out-going item packets
}
}
}
private net.minecraft.server.v1_7_R4.ItemStack maskItem(net.minecraft.server.v1_7_R4.ItemStack item)
{
if (item == null) return null; // Cannot mask a null item
CraftItemStack originalItem = CraftItemStack.asCraftMirror(item);
ItemMeta originalMeta = originalItem.getItemMeta();
if (originalMeta == null || originalMeta.getLore() == null) return item; // No need to modify item packets with no lore
List<String> lore = new ArrayList<String>();
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);
return newItem;
}
}

View File

@ -5,6 +5,7 @@ import mineplex.game.clans.items.attributes.ItemAttribute;
import mineplex.minecraft.game.core.damage.CustomDamageEvent;
import net.minecraft.server.v1_7_R4.Material;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
@ -44,7 +45,7 @@ public class ItemListener implements Listener
_plugin = plugin;
}
@EventHandler
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onGamemodeChange(PlayerGameModeChangeEvent event)
{
if (event.getNewGameMode() == GameMode.CREATIVE) // Entering creative mode
@ -54,6 +55,17 @@ public class ItemListener implements Listener
else if (event.getPlayer().getGameMode() == GameMode.CREATIVE) // Exiting creative mode
{
GearManager.getInstance().removeCreativePlayer(event.getPlayer());
// Update/refresh the players inventory in 5 ticks once they're in survival mode again
final Player player = event.getPlayer();
Bukkit.getScheduler().runTaskLater(_plugin, new Runnable()
{
@Override
public void run()
{
player.updateInventory();
}
}, 5l);
}
}
@ -163,21 +175,6 @@ public class ItemListener implements Listener
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event)
{
// Prevent players from equipping armour items in hand (prevents lore-breaking bug)
ItemStack itemInHand = event.getItem();
if (isArmour(itemInHand))
{
Action action = event.getAction();
boolean rightClick = action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK;
if (rightClick)
{
event.setCancelled(true);
GearManager.notify(event.getPlayer(), "You must manually equip armour!");
return;
}
}
// Activate weapon interact abilities
PlayerGear playerGear = getGear(event.getPlayer());
playerGear.onInteract(event);

View File

@ -58,6 +58,7 @@ public class GearCommand extends CommandBase<GearManager>
}
else
{
caller.updateInventory();
//caller.setFoodLevel(10);
//return;
}

View File

@ -94,7 +94,7 @@ public class Evade extends SkillActive
if (!_active.contains(cur))
continue;
if (!cur.isBlocking())
if (!UtilPlayer.isBlocking(cur))
{
_active.remove(cur);
continue;

View File

@ -143,7 +143,7 @@ public class Illusion extends SkillActive
Skeleton skel = _active.get(cur);
if (Factory.Condition().GetActiveCondition(cur, ConditionType.CLOAK) == null ||
!cur.isBlocking() ||
!UtilPlayer.isBlocking(cur) ||
!Factory.Energy().Use(cur, GetName(), 0.625 - (getLevel(cur) * 0.025), true, true) ||
skel == null ||
!skel.isValid())

View File

@ -201,7 +201,7 @@ public class BlockToss extends SkillCharge implements IThrown
}
//Throw
if (!cur.isBlocking())
if (!UtilPlayer.isBlocking(cur))
throwSet.add(cur);
//Charged Tick

View File

@ -293,7 +293,7 @@ public class DwarfToss extends SkillActive
}
//Expire
if (!cur.isBlocking() || System.currentTimeMillis() - _time.get(cur) > 5000)
if (!UtilPlayer.isBlocking(cur) || System.currentTimeMillis() - _time.get(cur) > 5000)
{
throwSet.add(cur);
}

View File

@ -94,7 +94,7 @@ public class FleshHook extends SkillActiveCharge implements IThrown
if (level == 0) return;
//Add Charge
if (cur.isBlocking())
if (UtilPlayer.isBlocking(cur))
{
Charge(cur);
}

View File

@ -19,6 +19,7 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.block.Action;
import org.bukkit.event.entity.ProjectileHitEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.util.Vector;
import mineplex.minecraft.game.classcombat.Class.IPvpClass.ClassType;
@ -87,7 +88,7 @@ public class Blizzard extends SkillActive
{
_active.add(player);
}
@EventHandler
public void Energy(UpdateEvent event)
{
@ -99,7 +100,7 @@ public class Blizzard extends SkillActive
if (!_active.contains(cur))
continue;
if (!cur.isBlocking())
if (!UtilPlayer.isBlocking(cur))
{
_active.remove(cur);
continue;
@ -134,7 +135,7 @@ public class Blizzard extends SkillActive
if (!_active.contains(cur))
continue;
if (!cur.isBlocking())
if (!UtilPlayer.isBlocking(cur))
{
_active.remove(cur);
continue;

View File

@ -85,7 +85,7 @@ public class Inferno extends SkillActive
if (!_active.contains(cur))
continue;
if (!cur.isBlocking())
if (!UtilPlayer.isBlocking(cur))
{
_active.remove(cur);
continue;

View File

@ -75,7 +75,7 @@ public class Magnetize extends SkillActive
if (!_active.contains(cur))
continue;
if (!cur.isBlocking())
if (!UtilPlayer.isBlocking(cur))
{
_active.remove(cur);
continue;

View File

@ -146,7 +146,7 @@ public class Rupture extends SkillActiveCharge
if (level == 0) return;
//Add Charge
if (!cur.isBlocking())
if (!UtilPlayer.isBlocking(cur))
DoRupture(cur);
else

View File

@ -6,8 +6,8 @@ import java.util.Set;
import mineplex.core.common.util.C;
import mineplex.core.common.util.NautHashMap;
import mineplex.core.common.util.UtilGear;
import mineplex.core.common.util.UtilPlayer;
import mineplex.core.common.util.UtilTextBottom;
import mineplex.minecraft.game.classcombat.Class.IPvpClass.ClassType;
import mineplex.minecraft.game.classcombat.Skill.repository.token.SkillToken;
@ -20,6 +20,7 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.event.player.PlayerQuitEvent;
public abstract class Skill implements ISkill, Listener
@ -292,6 +293,17 @@ public abstract class Skill implements ISkill, Listener
{
Reset(event.getEntity());
}
/**
* Trigger {@link UtilPlayer#onHotbarChange(Player)} to appropriately check
* if players are blocking.
* @param event
*/
@EventHandler
public void onHotbarChange(PlayerItemHeldEvent event)
{
UtilPlayer.onHotbarChange(event.getPlayer());
}
@EventHandler
public final void Quit(PlayerQuitEvent event)

View File

@ -1,6 +1,7 @@
package mineplex.minecraft.game.classcombat.Skill;
import mineplex.core.common.util.UtilEnt;
import mineplex.core.common.util.UtilPlayer;
import mineplex.core.recharge.Recharge;
import mineplex.core.updater.UpdateType;
import mineplex.core.updater.event.UpdateEvent;
@ -51,8 +52,8 @@ public abstract class SkillChargeSword extends SkillCharge implements Listener
for (Player cur : GetUsers())
{
//Charge
if (cur.isBlocking())
{
if (UtilPlayer.isBlocking(cur))
{
//Flags
if (!_canChargeInAir && !UtilEnt.isGrounded(cur))
continue;