diff --git a/src/main/java/net/tylermurphy/hideAndSeek/Main.java b/src/main/java/net/tylermurphy/hideAndSeek/Main.java index dc36752..9a589ae 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/Main.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/Main.java @@ -23,12 +23,9 @@ import net.tylermurphy.hideAndSeek.configuration.Config; import net.tylermurphy.hideAndSeek.configuration.Items; import net.tylermurphy.hideAndSeek.configuration.Localization; import net.tylermurphy.hideAndSeek.database.Database; -import net.tylermurphy.hideAndSeek.game.Board; -import net.tylermurphy.hideAndSeek.game.Disguiser; -import net.tylermurphy.hideAndSeek.game.PlayerLoader; +import net.tylermurphy.hideAndSeek.game.*; import net.tylermurphy.hideAndSeek.game.util.Status; import net.tylermurphy.hideAndSeek.util.CommandHandler; -import net.tylermurphy.hideAndSeek.game.Game; import net.tylermurphy.hideAndSeek.game.listener.*; import net.tylermurphy.hideAndSeek.util.PAPIExpansion; import net.tylermurphy.hideAndSeek.util.TabCompleter; @@ -55,46 +52,23 @@ public class Main extends JavaPlugin implements Listener { private static Main instance; private static int version; - private final Database database; - private final Board board; - private final Disguiser disguiser; - + private Database database; + private Board board; + private Disguiser disguiser; + private EntityHider entityHider; private Game game; - public Main() { - super(); - onConstructed(); - board = new Board(); - database = new Database(); - disguiser = new Disguiser(); - } - - protected Main(JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) { - super(loader, description, dataFolder, file); - onConstructed(); - board = new Board(); - database = new Database(); - disguiser = new Disguiser(); - } - - private void onConstructed(){ - - instance = this; - - Matcher matcher = Pattern.compile("MC: \\d\\.(\\d+)").matcher(Bukkit.getVersion()); - if (matcher.find()) { - version = Integer.parseInt(matcher.group(1)); - } else { - throw new IllegalArgumentException("Failed to parse server version from: " + Bukkit.getVersion()); - } - + public void onEnable() { + Main.instance = this; Config.loadConfig(); Localization.loadLocalization(); Items.loadItems(); - } - - public void onEnable() { + this.updateVersion(); + this.board = new Board(); + this.database = new Database(); + this.disguiser = new Disguiser(); + this.entityHider = new EntityHider(this, EntityHider.Policy.BLACKLIST); this.registerListeners(); CommandHandler.registerCommands(); @@ -142,6 +116,15 @@ public class Main extends JavaPlugin implements Listener { getServer().getPluginManager().registerEvents(new PlayerHandler(), this); getServer().getPluginManager().registerEvents(new RespawnHandler(), this); } + + private void updateVersion(){ + Matcher matcher = Pattern.compile("MC: \\d\\.(\\d+)").matcher(Bukkit.getVersion()); + if (matcher.find()) { + version = Integer.parseInt(matcher.group(1)); + } else { + throw new IllegalArgumentException("Failed to parse server version from: " + Bukkit.getVersion()); + } + } public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, String[] args) { return CommandHandler.handleCommand(sender, args); @@ -173,6 +156,8 @@ public class Main extends JavaPlugin implements Listener { public Disguiser getDisguiser() { return disguiser; } + public EntityHider getEntityHider() { return entityHider; } + public boolean supports(int v){ return version >= v; } diff --git a/src/main/java/net/tylermurphy/hideAndSeek/game/Board.java b/src/main/java/net/tylermurphy/hideAndSeek/game/Board.java index c6aaaf5..050faa8 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/game/Board.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/Board.java @@ -401,6 +401,8 @@ class CustomBoard { hiderTeam.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.NEVER); seekerTeam.setOption(Team.Option.NAME_TAG_VISIBILITY, Team.OptionStatus.NEVER); } + hiderTeam.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER); + seekerTeam.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER); } else { if (nameTagsVisible) { hiderTeam.setNameTagVisibility(NameTagVisibility.HIDE_FOR_OTHER_TEAMS); diff --git a/src/main/java/net/tylermurphy/hideAndSeek/game/Disguiser.java b/src/main/java/net/tylermurphy/hideAndSeek/game/Disguiser.java index b4f70ad..a8ac441 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/game/Disguiser.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/Disguiser.java @@ -1,62 +1,69 @@ package net.tylermurphy.hideAndSeek.game; +import net.tylermurphy.hideAndSeek.game.util.Disguise; +import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.entity.FallingBlock; import org.bukkit.entity.Player; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; +import org.bukkit.util.BlockVector; +import org.jetbrains.annotations.Nullable; import java.util.HashMap; import java.util.Map; +import java.util.Optional; +import java.util.Vector; public class Disguiser { - private final Map blocks; + private final Map disguises; public Disguiser(){ - this.blocks = new HashMap<>(); + this.disguises = new HashMap<>(); } - public FallingBlock getBlock(Player player){ - return blocks.get(player); + public Disguise getDisguise(Player player){ + return disguises.get(player); } - public boolean contains(FallingBlock block) { return blocks.containsValue(block); } + public boolean disguised(Player player) { return disguises.containsKey(player); } - public boolean disguised(Player player) { return blocks.containsKey(player); } + @Nullable + public Disguise getByEntityID(int ID){ + return disguises.values().stream().filter(disguise -> disguise.getEntityID() == ID).findFirst().orElse(null); + } + + @Nullable + public Disguise getByBlockLocation(BlockVector loc){ + return disguises.values().stream().filter(disguise -> { + if(disguise.getSolidLocation() == null) return false; + return disguise.getSolidLocation().toVector().toBlockVector() == loc; + }).findFirst().orElse(null); + } public void check(){ - for(Map.Entry set : blocks.entrySet()){ + for(Map.Entry set : disguises.entrySet()){ + Disguise disguise = set.getValue(); Player player = set.getKey(); - FallingBlock block = set.getValue(); - if(block.isDead()){ - block.remove(); - FallingBlock replacement = player.getLocation().getWorld().spawnFallingBlock(player.getLocation(), block.getMaterial(), (byte)0); - replacement.setGravity(false); - replacement.setDropItem(false); - blocks.put(player, replacement); + if(!player.isOnline()) { + disguise.remove(); + disguises.remove(player); + } else { + disguise.update(); } } } public void disguise(Player player, Material material){ - if(blocks.containsKey(player)){ - FallingBlock block = blocks.get(player); - block.remove(); + if(disguises.containsKey(player)){ + disguises.get(player).remove(); } - FallingBlock block = player.getLocation().getWorld().spawnFallingBlock(player.getLocation(), material, (byte)0); - block.setGravity(false); - block.setDropItem(false); - blocks.put(player, block); - player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 1000000, 0,false, false)); + Disguise disguise = new Disguise(player, material); + disguises.put(player, disguise); } public void reveal(Player player){ - if(!blocks.containsKey(player)) return; - FallingBlock block = blocks.get(player); - block.remove(); - blocks.remove(player); - player.removePotionEffect(PotionEffectType.INVISIBILITY); + if(disguises.containsKey(player)) + disguises.get(player).remove(); + disguises.remove(player); } } diff --git a/src/main/java/net/tylermurphy/hideAndSeek/game/EntityHider.java b/src/main/java/net/tylermurphy/hideAndSeek/game/EntityHider.java new file mode 100644 index 0000000..9dcb0ab --- /dev/null +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/EntityHider.java @@ -0,0 +1,282 @@ +package net.tylermurphy.hideAndSeek.game; + +import static com.comphenix.protocol.PacketType.Play.Server.*; + +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Map; + +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.world.ChunkUnloadEvent; +import org.bukkit.plugin.Plugin; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketAdapter; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.google.common.base.Preconditions; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; + +public class EntityHider implements Listener { + protected Table observerEntityMap = HashBasedTable.create(); + + private static final PacketType[] ENTITY_PACKETS = { + ENTITY_EQUIPMENT, BED, ANIMATION, NAMED_ENTITY_SPAWN, + COLLECT, SPAWN_ENTITY, SPAWN_ENTITY_LIVING, SPAWN_ENTITY_PAINTING, SPAWN_ENTITY_EXPERIENCE_ORB, + ENTITY_VELOCITY, REL_ENTITY_MOVE, ENTITY_LOOK, ENTITY_MOVE_LOOK, ENTITY_MOVE_LOOK, + ENTITY_TELEPORT, ENTITY_HEAD_ROTATION, ENTITY_STATUS, ATTACH_ENTITY, ENTITY_METADATA, + ENTITY_EFFECT, REMOVE_ENTITY_EFFECT, BLOCK_BREAK_ANIMATION + }; + + public enum Policy { + WHITELIST, + BLACKLIST, + } + + private ProtocolManager manager; + + private final Listener bukkitListener; + private final PacketAdapter protocolListener; + + protected final Policy policy; + + public EntityHider(Plugin plugin, Policy policy) { + Preconditions.checkNotNull(plugin, "plugin cannot be NULL."); + + // Save policy + this.policy = policy; + this.manager = ProtocolLibrary.getProtocolManager(); + + // Register events and packet listener + plugin.getServer().getPluginManager().registerEvents( + bukkitListener = constructBukkit(), plugin); + manager.addPacketListener( + protocolListener = constructProtocol(plugin)); + } + + /** + * Set the visibility status of a given entity for a particular observer. + * @param observer - the observer player. + * @param entityID - ID of the entity that will be hidden or made visible. + * @param visible - TRUE if the entity should be made visible, FALSE if not. + * @return TRUE if the entity was visible before this method call, FALSE otherwise. + */ + protected boolean setVisibility(Player observer, int entityID, boolean visible) { + switch (policy) { + case BLACKLIST: + // Non-membership means they are visible + return !setMembership(observer, entityID, !visible); + case WHITELIST: + return setMembership(observer, entityID, visible); + default : + throw new IllegalArgumentException("Unknown policy: " + policy); + } + } + + /** + * Add or remove the given entity and observer entry from the table. + * @param observer - the player observer. + * @param entityID - ID of the entity. + * @param member - TRUE if they should be present in the table, FALSE otherwise. + * @return TRUE if they already were present, FALSE otherwise. + */ + protected boolean setMembership(Player observer, int entityID, boolean member) { + if (member) { + return observerEntityMap.put(observer.getEntityId(), entityID, true) != null; + } else { + return observerEntityMap.remove(observer.getEntityId(), entityID) != null; + } + } + + /** + * Determine if the given entity and observer is present in the table. + * @param observer - the player observer. + * @param entityID - ID of the entity. + * @return TRUE if they are present, FALSE otherwise. + */ + protected boolean getMembership(Player observer, int entityID) { + return observerEntityMap.contains(observer.getEntityId(), entityID); + } + + /** + * Determine if a given entity is visible for a particular observer. + * @param observer - the observer player. + * @param entityID - ID of the entity that we are testing for visibility. + * @return TRUE if the entity is visible, FALSE otherwise. + */ + protected boolean isVisible(Player observer, int entityID) { + // If we are using a whitelist, presence means visibility - if not, the opposite is the case + boolean presence = getMembership(observer, entityID); + + return (policy == Policy.WHITELIST) == presence; + } + + /** + * Remove the given entity from the underlying map. + * @param entity - the entity to remove. + */ + protected void removeEntity(Entity entity) { + int entityID = entity.getEntityId(); + + for (Map maps : observerEntityMap.rowMap().values()) { + maps.remove(entityID); + } + } + + /** + * Invoked when a player logs out. + * @param player - the player that jused logged out. + */ + protected void removePlayer(Player player) { + // Cleanup + observerEntityMap.rowMap().remove(player.getEntityId()); + } + + /** + * Construct the Bukkit event listener. + * @return Our listener. + */ + private Listener constructBukkit() { + return new Listener() { + @EventHandler + public void onEntityDeath(EntityDeathEvent e) { + removeEntity(e.getEntity()); + } + + @EventHandler + public void onChunkUnload(ChunkUnloadEvent e) { + for (Entity entity : e.getChunk().getEntities()) { + removeEntity(entity); + } + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent e) { + removePlayer(e.getPlayer()); + } + }; + } + + /** + * Construct the packet listener that will be used to intercept every entity-related packet. + * @param plugin - the parent plugin. + * @return The packet listener. + */ + private PacketAdapter constructProtocol(Plugin plugin) { + return new PacketAdapter(plugin, ENTITY_PACKETS) { + @Override + public void onPacketSending(PacketEvent event) { + int entityID = event.getPacket().getIntegers().read(0); + + // See if this packet should be cancelled + if (!isVisible(event.getPlayer(), entityID)) { + event.setCancelled(true); + } + } + }; + } + + /** + * Toggle the visibility status of an entity for a player. + *

+ * If the entity is visible, it will be hidden. If it is hidden, it will become visible. + * @param observer - the player observer. + * @param entity - the entity to toggle. + * @return TRUE if the entity was visible before, FALSE otherwise. + */ + public final boolean toggleEntity(Player observer, Entity entity) { + if (isVisible(observer, entity.getEntityId())) { + return hideEntity(observer, entity); + } else { + return !showEntity(observer, entity); + } + } + + /** + * Allow the observer to see an entity that was previously hidden. + * @param observer - the observer. + * @param entity - the entity to show. + * @return TRUE if the entity was hidden before, FALSE otherwise. + */ + public final boolean showEntity(Player observer, Entity entity) { + validate(observer, entity); + boolean hiddenBefore = !setVisibility(observer, entity.getEntityId(), true); + + // Resend packets + if (manager != null && hiddenBefore) { + manager.updateEntity(entity, Arrays.asList(observer)); + } + return hiddenBefore; + } + + /** + * Prevent the observer from seeing a given entity. + * @param observer - the player observer. + * @param entity - the entity to hide. + * @return TRUE if the entity was previously visible, FALSE otherwise. + */ + public final boolean hideEntity(Player observer, Entity entity) { + validate(observer, entity); + boolean visibleBefore = setVisibility(observer, entity.getEntityId(), false); + + if (visibleBefore) { + PacketContainer destroyEntity = new PacketContainer(ENTITY_DESTROY); + try { + destroyEntity.getIntegerArrays().write(0, new int[]{entity.getEntityId()}); + } catch (Exception e){ return false; } + // Make the entity disappear + try { + manager.sendServerPacket(observer, destroyEntity); + } catch (InvocationTargetException e) { + throw new RuntimeException("Cannot send server packet.", e); + } + } + return visibleBefore; + } + + /** + * Determine if the given entity has been hidden from an observer. + *

+ * Note that the entity may very well be occluded or out of range from the perspective + * of the observer. This method simply checks if an entity has been completely hidden + * for that observer. + * @param observer - the observer. + * @param entity - the entity that may be hidden. + * @return TRUE if the player may see the entity, FALSE if the entity has been hidden. + */ + public final boolean canSee(Player observer, Entity entity) { + validate(observer, entity); + + return isVisible(observer, entity.getEntityId()); + } + + private void validate(Player observer, Entity entity) { + Preconditions.checkNotNull(observer, "observer cannot be NULL."); + Preconditions.checkNotNull(entity, "entity cannot be NULL."); + } + + /** + * Retrieve the current visibility policy. + * @return The current visibility policy. + */ + public Policy getPolicy() { + return policy; + } + + public void close() { + if (manager != null) { + HandlerList.unregisterAll(bukkitListener); + manager.removePacketListener(protocolListener); + manager = null; + } + } +} diff --git a/src/main/java/net/tylermurphy/hideAndSeek/game/PlayerLoader.java b/src/main/java/net/tylermurphy/hideAndSeek/game/PlayerLoader.java index 2ddfeca..0447111 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/game/PlayerLoader.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/PlayerLoader.java @@ -99,6 +99,7 @@ public class PlayerLoader { public static void unloadPlayer(Player player){ player.setGameMode(GameMode.ADVENTURE); player.getInventory().clear(); + Main.getInstance().getDisguiser().reveal(player); for(PotionEffect effect : player.getActivePotionEffects()) { player.removePotionEffect(effect.getType()); } @@ -135,6 +136,8 @@ public class PlayerLoader { player.setGameMode(GameMode.ADVENTURE); player.getInventory().clear(); for(PotionEffect effect : player.getActivePotionEffects()) { + Main.getInstance().getLogger().severe(player.getName() + " " + effect.getType()); + if(effect.getType().getName().equals("INVISIBILITY") && Main.getInstance().getDisguiser().disguised(player)) continue; player.removePotionEffect(effect.getType()); } player.setFoodLevel(20); diff --git a/src/main/java/net/tylermurphy/hideAndSeek/game/listener/DamageHandler.java b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/DamageHandler.java index f09e112..cb4cba5 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/game/listener/DamageHandler.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/DamageHandler.java @@ -25,10 +25,8 @@ public class DamageHandler implements Listener { @EventHandler(priority = EventPriority.HIGHEST) public void onEntityDamage(EntityDamageEvent event) { - Board board = Main.getInstance().getBoard(); Game game = Main.getInstance().getGame(); - // If you are not a player, get out of here if (!(event.getEntity() instanceof Player)) return; // Define variables @@ -46,6 +44,7 @@ public class DamageHandler implements Listener { } // Makes sure that if there was an attacking player, that the event is allowed for the game if (attacker != null) { + System.out.println(event.getFinalDamage() + " " + player.getDisplayName() + " " + attacker.getDisplayName()); // Cancel if one player is in the game but other isn't if ((board.contains(player) && !board.contains(attacker)) || (!board.contains(player) && board.contains(attacker))) { event.setCancelled(true); diff --git a/src/main/java/net/tylermurphy/hideAndSeek/game/listener/DisguiseHandler.java b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/DisguiseHandler.java index e3eb341..af58f91 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/game/listener/DisguiseHandler.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/DisguiseHandler.java @@ -1,78 +1,200 @@ package net.tylermurphy.hideAndSeek.game.listener; -import com.comphenix.protocol.PacketType; +import static com.comphenix.protocol.PacketType.Play.Client.*; + import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.wrappers.BlockPosition; +import com.cryptomorin.xseries.XMaterial; import net.tylermurphy.hideAndSeek.Main; +import net.tylermurphy.hideAndSeek.game.util.Disguise; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.entity.FallingBlock; +import org.bukkit.Material; +import org.bukkit.attribute.Attribute; +import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.util.BlockVector; -import java.lang.reflect.InvocationTargetException; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.util.ArrayList; +import java.util.List; public class DisguiseHandler implements Listener { private static final ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + private final PacketAdapter packetListener; - private final Map locations = new HashMap<>(); - private final Map times = new HashMap<>(); + public DisguiseHandler(){ + packetListener = createProtocol(); + protocolManager.addPacketListener(packetListener); + } @EventHandler(priority = EventPriority.HIGHEST) public void onMove(PlayerMoveEvent event) { - checkStandingStill(event.getPlayer()); - FallingBlock block = Main.getInstance().getDisguiser().getBlock(event.getPlayer()); - if(block == null) return; - UUID uuid = event.getPlayer().getUniqueId(); - boolean finalFixLocation = times.containsKey(uuid) && new Date().getTime()-times.get(uuid) > 1000; - Bukkit.getOnlinePlayers().forEach(player -> { - teleportEntity(player, block, event.getPlayer().getLocation(), finalFixLocation); - }); + final Disguise disguise = Main.getInstance().getDisguiser().getDisguise(event.getPlayer()); + if(disguise == null) return; + final Location lastLocation = event.getPlayer().getLocation(); + Bukkit.getScheduler().scheduleSyncDelayedTask(Main.getInstance(), () -> { + final Location currentLocation = event.getPlayer().getLocation(); + if(lastLocation.getWorld() != currentLocation.getWorld()) return; + double distance = lastLocation.distance(currentLocation); + disguise.setSolidify(distance < .1); + }, 40L); + if(event.getFrom().distance(event.getTo()) > .1) + disguise.setSolidify(false); } - private void checkStandingStill(Player player){ - UUID uuid = player.getUniqueId(); - Location lastLoc = locations.get(uuid); - Location currentLoc = player.getLocation(); - if(lastLoc == null) lastLoc = currentLoc; - double distance = lastLoc.distance(currentLoc); - if(distance < .05){ - if(!times.containsKey(uuid)) - times.put(uuid, new Date().getTime()); +// @EventHandler(priority = EventPriority.MONITOR) +// public void onInteract(PlayerInteractEvent event) { +// Action action = event.getAction(); +// Player player = event.getPlayer(); +// Block block = event. +// } + + private PacketAdapter createProtocol(){ + return new PacketAdapter(Main.getInstance(), USE_ITEM, USE_ENTITY) { + + @Override + public void onPacketReceiving(PacketEvent event){ + PacketContainer packet = event.getPacket(); + Player player = event.getPlayer(); +// if(!Main.getInstance().getBoard().isSeeker(player)) return; + if(packet.getType() == USE_ITEM) { + System.out.print("\nUse Item: "); + BlockPosition data; + try { data = packet.getBlockPositionModifier().read(0); } + catch (Exception e) { return; } + System.out.print(data + " "); + BlockVector loc = new BlockVector(data.getX(), data.getY(), data.getZ()); + System.out.print(loc + " "); + Disguise disguise = Main.getInstance().getDisguiser().getByBlockLocation(loc); + System.out.print("FOUND"); + handleAttack(disguise, player); + } else if(packet.getType() == USE_ENTITY) { + System.out.print("\nUse Entity: "); + int id = packet.getIntegers().read(0); + System.out.print(id + " "); + Disguise disguise = Main.getInstance().getDisguiser().getByEntityID(id); + System.out.print("FOUND"); + handleAttack(disguise, player); + } + } + + }; + } + + private final List debounce = new ArrayList<>(); + + private void handleAttack(Disguise disguise, Player seeker){ + + double amount; + if(Main.getInstance().supports(9)) { + amount = seeker.getAttribute(Attribute.GENERIC_ATTACK_DAMAGE).getValue(); } else { - times.remove(uuid); + amount = getItemDamageValue(seeker.getItemInHand(), disguise.getPlayer(), seeker); } - locations.put(uuid, currentLoc); + + if(disguise == null) return; + disguise.setSolidify(false); + if(debounce.contains(disguise.getPlayer())) return; + debounce.add(disguise.getPlayer()); + Bukkit.getScheduler().scheduleSyncDelayedTask(Main.getInstance(), () -> { + EntityDamageByEntityEvent event = + new EntityDamageByEntityEvent(seeker, disguise.getPlayer(), EntityDamageEvent.DamageCause.ENTITY_ATTACK, amount); + event.setDamage(amount); + disguise.getPlayer().setLastDamageCause(event); + Main.getInstance().getServer().getPluginManager().callEvent(event); + if(!event.isCancelled()){ + disguise.getPlayer().damage(amount); + disguise.getPlayer().setVelocity(seeker.getLocation().getDirection().setY(.2).multiply(1)); + } + + }, 0); + Bukkit.getScheduler().scheduleSyncDelayedTask(Main.getInstance(), () -> debounce.remove(disguise.getPlayer()), 10); } - private void teleportEntity(Player player, FallingBlock block, Location location, boolean fixLocation) { - PacketContainer packet = protocolManager.createPacket(PacketType.Play.Server.ENTITY_TELEPORT); - packet.getModifier().writeDefaults(); - packet.getIntegers().write(0, block.getEntityId()); - if(fixLocation){ - packet.getDoubles().write(0, Math.round(location.getX()+.5)-.5); - packet.getDoubles().write(1, (double)Math.round(location.getY())); - packet.getDoubles().write(2, Math.round(location.getZ()+.5)-.5); - } else { - packet.getDoubles().write(0, location.getX()); - packet.getDoubles().write(1, location.getY()); - packet.getDoubles().write(2, location.getZ()); + private int getItemDamageValue(ItemStack is, Player damaged, Player attacker) { + double damageValue = 0; + if (is != null) { + if (is.getType() == XMaterial.WOODEN_SWORD.parseMaterial()) { + damageValue = 5; + } else if (is.getType() == Material.STONE_SWORD) { + damageValue = 6; + } else if (is.getType() == Material.IRON_SWORD) { + damageValue = 7; + } else if (is.getType() == Material.DIAMOND_SWORD) { + damageValue = 8; + } else { + damageValue = 1; + } + damageValue += is.getEnchantmentLevel(Enchantment.DAMAGE_ALL); } - try { - protocolManager.sendServerPacket(player, packet); - } catch (InvocationTargetException e) { - e.printStackTrace(); + if (damaged != null) { + Inventory i = damaged.getInventory(); + Material helmet = i.getItem(39).getType(); + Material chestplate = i.getItem(40).getType(); + Material leggings = i.getItem(41).getType(); + Material boots = i.getItem(42).getType(); + if (helmet == Material.LEATHER_HELMET) + damageValue -= (0.5 / 1.5); + // value shown at bar above the health bar / 1.5 + else if (helmet == Material.CHAINMAIL_HELMET + || helmet == Material.IRON_HELMET + || helmet == Material.DIAMOND_HELMET + || helmet == XMaterial.GOLDEN_HELMET.parseMaterial()) + damageValue -= (1 / 1.5); + + if (chestplate == Material.LEATHER_CHESTPLATE) + damageValue -= (1.0); + else if (chestplate == Material.CHAINMAIL_CHESTPLATE + || chestplate == XMaterial.GOLDEN_CHESTPLATE.parseMaterial()) + damageValue -= (2.5 / 1.5); + else if (chestplate == Material.IRON_CHESTPLATE) + damageValue -= (3 / 1.5); + else if (chestplate == Material.DIAMOND_CHESTPLATE) + damageValue -= (4 / 1.5); + + if (leggings == Material.LEATHER_LEGGINGS) + damageValue -= (1 / 1.5); + else if (leggings == XMaterial.GOLDEN_LEGGINGS.parseMaterial()) + damageValue -= (1.0); + else if (leggings == Material.CHAINMAIL_LEGGINGS) + damageValue -= (2 / 1.5); + else if (leggings == Material.IRON_LEGGINGS) + damageValue -= (2.5 / 1.5); + else if (leggings == Material.DIAMOND_LEGGINGS) + damageValue -= (3 / 1.5); + + if (boots == Material.LEATHER_BOOTS + || boots == XMaterial.GOLDEN_BOOTS.parseMaterial() + || boots == Material.CHAINMAIL_BOOTS) + damageValue -= (0.5 / 1.5); + else if (boots == Material.IRON_BOOTS) + damageValue -= (1 / 1.5); + else if (boots == Material.DIAMOND_BOOTS) + damageValue -= (1.0); } + + for (PotionEffect effect : attacker.getActivePotionEffects()){ + if (effect.getType() == PotionEffectType.HARM) { + damageValue += effect.getAmplifier()*1.5; + } + } + + return (int) Math.round(Math.max(damageValue, 0.0)); } - } diff --git a/src/main/java/net/tylermurphy/hideAndSeek/game/util/Disguise.java b/src/main/java/net/tylermurphy/hideAndSeek/game/util/Disguise.java new file mode 100644 index 0000000..691037b --- /dev/null +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/util/Disguise.java @@ -0,0 +1,143 @@ +package net.tylermurphy.hideAndSeek.game.util; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.wrappers.BlockPosition; +import com.comphenix.protocol.wrappers.WrappedBlockData; +import net.tylermurphy.hideAndSeek.Main; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.FallingBlock; +import org.bukkit.entity.Player; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.InvocationTargetException; + +public class Disguise { + + private static final ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + + final Player hider; + final Material material; + FallingBlock entity; + Location solidLocation; + boolean solid, solidify; + + public Disguise(Player player, Material material){ + this.hider = player; + this.material = material; + this.solid = false; + respawnEntity(); + player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 1000000, 0,false, false)); + } + + public void remove(){ + if(entity != null) + entity.remove(); + if(solid) + sendBlockUpdate(Material.AIR); + hider.removePotionEffect(PotionEffectType.INVISIBILITY); + } + + @Nullable + public Location getSolidLocation() { + return solidLocation; + } + + public int getEntityID() { + if(entity == null) return -1; + return entity.getEntityId(); + } + + public Player getPlayer() { + return hider; + } + + public boolean isSolid(){ + return solid; + } + + public void update(){ + + if(entity == null || entity.isDead()){ + if(entity != null) entity.remove(); + respawnEntity(); + } + + if(solidify){ + if(!solid) + solidLocation = hider.getLocation().getBlock().getLocation(); + solid = true; + sendBlockUpdate(material); + } else if(solid){ + solid = false; + sendBlockUpdate(Material.AIR); + } + sendToggleFallingBlock(!solid); + sendFallingBlockUpdate(); + } + + public void setSolidify(boolean value){ + this.solidify = value; + } + + private void sendBlockUpdate(Material material){ + final PacketContainer packet = protocolManager.createPacket(PacketType.Play.Server.BLOCK_CHANGE); + packet.getModifier().writeDefaults(); + packet.getBlockPositionModifier().write(0, new BlockPosition(solidLocation.toVector())); + packet.getBlockData().write(0, WrappedBlockData.createData(material)); + Bukkit.getOnlinePlayers().forEach(receiver -> { + if(receiver == hider) return; + try { + protocolManager.sendServerPacket(receiver, packet); + } catch (InvocationTargetException ignored) {} + }); + } + + private void sendFallingBlockUpdate() { + if(entity == null || entity.isDead()){ + if(entity != null) entity.remove(); + respawnEntity(); + } + final PacketContainer packet = protocolManager.createPacket(PacketType.Play.Server.ENTITY_TELEPORT); + Location location = hider.getLocation(); + packet.getModifier().writeDefaults(); + packet.getIntegers().write(0, entity.getEntityId()); + if(solid){ + packet.getDoubles().write(0, Math.round(location.getX()+.5)-.5); + packet.getDoubles().write(1, (double)Math.round(location.getY())); + packet.getDoubles().write(2, Math.round(location.getZ()+.5)-.5); + } else { + packet.getDoubles().write(0, location.getX()); + packet.getDoubles().write(1, location.getY()); + packet.getDoubles().write(2, location.getZ()); + } + Bukkit.getOnlinePlayers().forEach(receiver -> { + try { + protocolManager.sendServerPacket(receiver, packet); + } catch (InvocationTargetException ignored) {} + }); + } + + private void sendToggleFallingBlock(boolean show){ + Bukkit.getOnlinePlayers().forEach(receiver -> { + if(receiver == hider) return; + if(show) + Main.getInstance().getEntityHider().showEntity(receiver, entity); + else + Main.getInstance().getEntityHider().hideEntity(receiver, entity); + }); + } + + private void respawnEntity(){ + entity = hider.getLocation().getWorld().spawnFallingBlock(hider.getLocation(), material, (byte)0); + entity.setGravity(false); + entity.setDropItem(false); + } + +} \ No newline at end of file