diff options
Diffstat (limited to '')
12 files changed, 820 insertions, 24 deletions
diff --git a/src/main/java/net/tylermurphy/hideAndSeek/game/Board.java b/src/main/java/net/tylermurphy/hideAndSeek/game/Board.java index c6aaaf5..6bf0bfb 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/game/Board.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/Board.java @@ -35,6 +35,7 @@ import java.util.stream.Collectors; import static net.tylermurphy.hideAndSeek.configuration.Config.*; import static net.tylermurphy.hideAndSeek.configuration.Localization.message; +@SuppressWarnings("deprecation") public class Board { private final List<String> Hider = new ArrayList<>(), Seeker = new ArrayList<>(), Spectator = new ArrayList<>(); @@ -103,6 +104,9 @@ public class Board { } public void addHider(Player player) { + if(!Main.getInstance().supports(9)){ + player.spigot().setCollidesWithEntities(false); + } Hider.add(player.getUniqueId().toString()); Seeker.remove(player.getUniqueId().toString()); Spectator.remove(player.getUniqueId().toString()); @@ -110,6 +114,9 @@ public class Board { } public void addSeeker(Player player) { + if(!Main.getInstance().supports(9)){ + player.spigot().setCollidesWithEntities(false); + } Hider.remove(player.getUniqueId().toString()); Seeker.add(player.getUniqueId().toString()); Spectator.remove(player.getUniqueId().toString()); @@ -117,6 +124,9 @@ public class Board { } public void addSpectator(Player player) { + if(!Main.getInstance().supports(9)){ + player.spigot().setCollidesWithEntities(false); + } Hider.remove(player.getUniqueId().toString()); Seeker.remove(player.getUniqueId().toString()); Spectator.add(player.getUniqueId().toString()); @@ -124,6 +134,9 @@ public class Board { } public void remove(Player player) { + if(!Main.getInstance().supports(9)){ + player.spigot().setCollidesWithEntities(true); + } Hider.remove(player.getUniqueId().toString()); Seeker.remove(player.getUniqueId().toString()); Spectator.remove(player.getUniqueId().toString()); @@ -351,6 +364,7 @@ public class Board { } +@SuppressWarnings("deprecation") class CustomBoard { private final Scoreboard board; @@ -401,6 +415,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 new file mode 100644 index 0000000..2f8cbf6 --- /dev/null +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/Disguiser.java @@ -0,0 +1,72 @@ +package net.tylermurphy.hideAndSeek.game; + +import static net.tylermurphy.hideAndSeek.configuration.Config.*; + +import net.tylermurphy.hideAndSeek.game.util.Disguise; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +public class Disguiser { + + private final Map<Player, Disguise> disguises; + + public Disguiser(){ + this.disguises = new HashMap<>(); + } + + public Disguise getDisguise(Player player){ + return disguises.get(player); + } + + public boolean disguised(Player player) { return disguises.containsKey(player); } + + @Nullable + public Disguise getByEntityID(int ID){ + return disguises.values().stream().filter(disguise -> disguise.getEntityID() == ID).findFirst().orElse(null); + } + + @Nullable + public Disguise getByHitBoxID(int ID){ + return disguises.values().stream().filter(disguise -> disguise.getHitBoxID() == ID).findFirst().orElse(null); + } + + public void check(){ + for(Map.Entry<Player, Disguise> set : disguises.entrySet()){ + Disguise disguise = set.getValue(); + Player player = set.getKey(); + if(!player.isOnline()) { + disguise.remove(); + disguises.remove(player); + } else { + disguise.update(); + } + } + } + + public void disguise(Player player, Material material){ + if(!blockhuntEnabled){ + player.sendMessage(errorPrefix + "Please enable blockhunt in config.yml to enable disguises. Blockhunt does not work on 1.8"); + return; + } + if(disguises.containsKey(player)){ + disguises.get(player).remove(); + } + Disguise disguise = new Disguise(player, material); + disguises.put(player, disguise); + } + + public void reveal(Player player){ + if(disguises.containsKey(player)) + disguises.get(player).remove(); + disguises.remove(player); + } + + public void cleanUp() { + disguises.values().forEach(Disguise::remove); + } + +} 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..8274655 --- /dev/null +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/EntityHider.java @@ -0,0 +1,286 @@ +package net.tylermurphy.hideAndSeek.game; + +import static com.comphenix.protocol.PacketType.Play.Server.*; + +import java.lang.reflect.InvocationTargetException; +import java.util.Collections; +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<Integer, Integer, Boolean> observerEntityMap = HashBasedTable.create(); + + private static final PacketType[] ENTITY_PACKETS = { + ENTITY_EQUIPMENT, 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_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<Integer, Boolean> 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. + * <p> + * 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. + */ + @SuppressWarnings("unused") + 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, Collections.singletonList(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. + * <p> + * 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. + */ + @SuppressWarnings("unused") + 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. + */ + @SuppressWarnings("unused") + public Policy getPolicy() { + return policy; + } + + @SuppressWarnings("unused") + public void close() { + if (manager != null) { + HandlerList.unregisterAll(bukkitListener); + manager.removePacketListener(protocolListener); + manager = null; + } + } +} diff --git a/src/main/java/net/tylermurphy/hideAndSeek/game/Game.java b/src/main/java/net/tylermurphy/hideAndSeek/game/Game.java index 87fa4a2..b3fd457 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/game/Game.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/Game.java @@ -107,7 +107,8 @@ public class Game { public void start() { try { Optional<Player> rand = board.getPlayers().stream().skip(new Random().nextInt(board.size())).findFirst(); - String seekerName = rand.get().getName(); + Player picked = rand.orElse(board.getPlayers().get(0)); + String seekerName = picked.getName(); Player temp = Bukkit.getPlayer(seekerName); Player seeker = board.getPlayer(temp.getUniqueId()); start(seeker); @@ -210,6 +211,7 @@ public class Game { handleBungeeLeave(player); } + @SuppressWarnings("UnstableApiUsage") private void handleBungeeLeave(Player player) { if (bungeeLeave) { ByteArrayDataOutput out = ByteStreams.newDataOutput(); diff --git a/src/main/java/net/tylermurphy/hideAndSeek/game/PlayerLoader.java b/src/main/java/net/tylermurphy/hideAndSeek/game/PlayerLoader.java index bd2a965..bd35ab5 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/game/PlayerLoader.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/PlayerLoader.java @@ -19,7 +19,6 @@ package net.tylermurphy.hideAndSeek.game; -import com.cryptomorin.xseries.XItemStack; import com.cryptomorin.xseries.messages.Titles; import net.md_5.bungee.api.ChatColor; import net.tylermurphy.hideAndSeek.Main; @@ -30,6 +29,7 @@ import org.bukkit.Location; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeInstance; import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; @@ -38,6 +38,7 @@ import static net.tylermurphy.hideAndSeek.configuration.Config.*; import static net.tylermurphy.hideAndSeek.configuration.Config.lobbyPosition; import static net.tylermurphy.hideAndSeek.configuration.Localization.message; +@SuppressWarnings("deprecation") public class PlayerLoader { public static void loadHider(Player player, String gameWorld){ @@ -45,6 +46,9 @@ public class PlayerLoader { loadPlayer(player); player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED,1000000,5,false,false)); Titles.sendTitle(player, 10, 70, 20, ChatColor.WHITE + "" + message("HIDER_TEAM_NAME"), ChatColor.WHITE + message("HIDERS_SUBTITLE").toString()); + if(blockhuntEnabled){ + openBlockHuntPicker(player); + } } public static void loadSeeker(Player player, String gameWorld){ @@ -64,9 +68,7 @@ public class PlayerLoader { player.setFallDistance(0.0F); player.getInventory().setItem(flightToggleItemPosition, flightToggleItem); player.getInventory().setItem(teleportItemPosition, teleportItem); - Main.getInstance().getBoard().getPlayers().forEach(otherPlayer -> { - otherPlayer.hidePlayer(player); - }); + Main.getInstance().getBoard().getPlayers().forEach(otherPlayer -> otherPlayer.hidePlayer(player)); Titles.sendTitle(player, 10, 70, 20, ChatColor.GRAY + "" + ChatColor.BOLD + "SPECTATING", ChatColor.WHITE + message("SPECTATOR_SUBTITLE").toString()); } @@ -94,6 +96,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()); } @@ -130,6 +133,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); @@ -141,4 +146,13 @@ public class PlayerLoader { } } + public static void openBlockHuntPicker(Player player){ + int slots = ((blockhuntBlocks.size()-1)/9)*9+9; + Inventory inventory = Main.getInstance().getServer().createInventory(null, slots, "Select a Block"); + for(int i=0;i<blockhuntBlocks.size();i++){ + inventory.setItem(i, new ItemStack(blockhuntBlocks.get(i))); + } + player.openInventory(inventory); + } + } 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 19ec83a..cb4cba5 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/game/listener/DamageHandler.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/DamageHandler.java @@ -15,6 +15,7 @@ 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.entity.PlayerDeathEvent; import static net.tylermurphy.hideAndSeek.configuration.Config.*; import static net.tylermurphy.hideAndSeek.configuration.Config.spawnPosition; @@ -24,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 @@ -45,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); @@ -94,6 +94,8 @@ public class DamageHandler implements Listener { } else { XSound.ENTITY_PLAYER_HURT.play(player, 1, 1); } + // Reveal player if they are disguised + Main.getInstance().getDisguiser().reveal(player); // Teleport player to seeker spawn player.teleport(new Location(Bukkit.getWorld(game.getGameWorld()), spawnPosition.getX(), spawnPosition.getY(), spawnPosition.getZ())); // Add leaderboard stats @@ -115,4 +117,9 @@ public class DamageHandler implements Listener { board.reloadBoardTeams(); } + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerDeath(PlayerDeathEvent event){ + Main.getInstance().getDisguiser().reveal(event.getEntity()); + } + } diff --git a/src/main/java/net/tylermurphy/hideAndSeek/game/listener/DisguiseHandler.java b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/DisguiseHandler.java new file mode 100644 index 0000000..10db324 --- /dev/null +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/DisguiseHandler.java @@ -0,0 +1,187 @@ +package net.tylermurphy.hideAndSeek.game.listener; + +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.cryptomorin.xseries.XMaterial; +import net.tylermurphy.hideAndSeek.Main; +import net.tylermurphy.hideAndSeek.game.util.Disguise; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +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 java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("deprecation") +public class DisguiseHandler implements Listener { + + private static final ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager(); + + public DisguiseHandler(){ + protocolManager.addPacketListener(createProtocol()); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onMove(PlayerMoveEvent event) { + 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); + } + +// @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_ENTITY) { + + @Override + public void onPacketReceiving(PacketEvent event){ + PacketContainer packet = event.getPacket(); + Player player = event.getPlayer(); +// if(!Main.getInstance().getBoard().isSeeker(player)) return; + int id = packet.getIntegers().read(0); + Disguise disguise = Main.getInstance().getDisguiser().getByEntityID(id); + if(disguise == null) disguise = Main.getInstance().getDisguiser().getByHitBoxID(id); + if(disguise == null) return; + if(disguise.getPlayer().getGameMode() == GameMode.CREATIVE) return; + event.setCancelled(true); + handleAttack(disguise, player); + } + }; + } + + private final List<Player> debounce = new ArrayList<>(); + + private void handleAttack(Disguise disguise, Player seeker){ + + if(disguise.getPlayer() == seeker) return; + + double amount; + if(Main.getInstance().supports(9)) { + amount = seeker.getAttribute(Attribute.GENERIC_ATTACK_DAMAGE).getValue(); + } else { + amount = getItemDamageValue(seeker.getItemInHand(), disguise.getPlayer(), seeker); + } + + 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 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); + } + + 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/listener/InteractHandler.java b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/InteractHandler.java index c8dd488..3440780 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/game/listener/InteractHandler.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/InteractHandler.java @@ -22,6 +22,7 @@ import static net.tylermurphy.hideAndSeek.configuration.Config.*; import static net.tylermurphy.hideAndSeek.configuration.Config.glowPowerupItem; import static net.tylermurphy.hideAndSeek.configuration.Localization.message; +@SuppressWarnings("deprecation") public class InteractHandler implements Listener { @EventHandler(priority = EventPriority.HIGHEST) @@ -96,25 +97,21 @@ public class InteractHandler implements Listener { int amount = Main.getInstance().getBoard().getHiders().size() + Main.getInstance().getBoard().getSeekers().size(); Inventory teleportMenu = Main.getInstance().getServer().createInventory(null, 9*(((amount-1)/9)+1), ChatColor.stripColor(teleportItem.getItemMeta().getDisplayName())); List<String> hider_lore = new ArrayList<>(); hider_lore.add(message("HIDER_TEAM_NAME").toString()); - Main.getInstance().getBoard().getHiders().forEach(hider -> { - teleportMenu.addItem(getSkull(hider, hider_lore)); - }); + Main.getInstance().getBoard().getHiders().forEach(hider -> teleportMenu.addItem(getSkull(hider, hider_lore))); List<String> seeker_lore = new ArrayList<>(); seeker_lore.add(message("SEEKER_TEAM_NAME").toString()); - Main.getInstance().getBoard().getSeekers().forEach(seeker -> { - teleportMenu.addItem(getSkull(seeker, seeker_lore)); - }); + Main.getInstance().getBoard().getSeekers().forEach(seeker -> teleportMenu.addItem(getSkull(seeker, seeker_lore))); event.getPlayer().openInventory(teleportMenu); } } private ItemStack getSkull(Player player, List<String> lore){ assert XMaterial.PLAYER_HEAD.parseMaterial() != null; - ItemStack playerhead = new ItemStack(XMaterial.PLAYER_HEAD.parseMaterial(), 1, (byte) 3); - SkullMeta playerheadmeta = (SkullMeta) playerhead.getItemMeta(); - playerheadmeta.setOwner(player.getName()); - playerheadmeta.setDisplayName(player.getName()); - playerheadmeta.setLore(lore); - playerhead.setItemMeta(playerheadmeta); - return playerhead; + ItemStack playerHead = new ItemStack(XMaterial.PLAYER_HEAD.parseMaterial(), 1, (byte) 3); + SkullMeta playerHeadMeta = (SkullMeta) playerHead.getItemMeta(); + playerHeadMeta.setOwner(player.getName()); + playerHeadMeta.setDisplayName(player.getName()); + playerHeadMeta.setLore(lore); + playerHead.setItemMeta(playerHeadMeta); + return playerHead; } } diff --git a/src/main/java/net/tylermurphy/hideAndSeek/game/listener/InventoryHandler.java b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/InventoryHandler.java index f0fb4f9..e72bea0 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/game/listener/InventoryHandler.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/InventoryHandler.java @@ -19,26 +19,29 @@ package net.tylermurphy.hideAndSeek.game.listener; +import static net.tylermurphy.hideAndSeek.configuration.Config.*; + import com.cryptomorin.xseries.XMaterial; import net.tylermurphy.hideAndSeek.Main; import net.tylermurphy.hideAndSeek.command.Debug; import net.tylermurphy.hideAndSeek.game.util.Status; +import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; public class InventoryHandler implements Listener { @EventHandler(priority = EventPriority.HIGHEST) public void onInventoryClick(InventoryClickEvent event) { if (!(event.getWhoClicked() instanceof Player)) return; - - Player player = (Player) event.getWhoClicked(); checkForInventoryMove(event); checkForSpectatorTeleportMenu(event); checkForDebugMenu(event); + checkForBlockHuntMenu(event); } private void checkForInventoryMove(InventoryClickEvent event){ @@ -74,4 +77,38 @@ public class InventoryHandler implements Listener { } } + private void checkForBlockHuntMenu(InventoryClickEvent event){ + boolean test; + if(Main.getInstance().supports(14)){ + test = event.getView().getTitle().equals("Select a Block"); + } else { + test = event.getInventory().getName().equals("Select a Block"); + } + if(!test) return; + event.setCancelled(true); + Material mat = blockhuntBlocks.get(event.getRawSlot()); + if(mat == null) return; + Player player = (Player) event.getWhoClicked(); + Main.getInstance().getDisguiser().disguise(player, mat); + player.closeInventory(); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onInventoryClose(InventoryCloseEvent event){ + if (!(event.getPlayer() instanceof Player)) return; + boolean test; + if(Main.getInstance().supports(14)){ + test = event.getView().getTitle().equals("Select a Block"); + } else { + test = event.getInventory().getName().equals("Select a Block"); + } + if(!test) return; + Material mat = blockhuntBlocks.get(0); + if(mat == null) return; + Player player = (Player) event.getPlayer(); + if(Main.getInstance().getDisguiser().disguised(player)) return; + Main.getInstance().getDisguiser().disguise(player, mat); + player.closeInventory(); + } + } diff --git a/src/main/java/net/tylermurphy/hideAndSeek/game/listener/JoinLeaveHandler.java b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/JoinLeaveHandler.java index 5233d2e..6d88ebf 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/game/listener/JoinLeaveHandler.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/JoinLeaveHandler.java @@ -15,7 +15,6 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.ItemStack; -import org.bukkit.potion.PotionEffect; import static net.tylermurphy.hideAndSeek.configuration.Config.*; import static net.tylermurphy.hideAndSeek.configuration.Config.exitPosition; @@ -56,6 +55,10 @@ public class JoinLeaveHandler implements Listener { @EventHandler(priority = EventPriority.MONITOR) public void onKick(PlayerKickEvent event) { + if(event.getReason().equals("Flying is not enabled on this server!")){ + event.setCancelled(true); + return; + } handleLeave(event.getPlayer()); } diff --git a/src/main/java/net/tylermurphy/hideAndSeek/game/listener/MovementHandler.java b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/MovementHandler.java index 7a7e8ae..e6e32f6 100644 --- a/src/main/java/net/tylermurphy/hideAndSeek/game/listener/MovementHandler.java +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/listener/MovementHandler.java @@ -1,6 +1,5 @@ package net.tylermurphy.hideAndSeek.game.listener; -import com.comphenix.protocol.PacketType; import com.google.common.collect.Sets; import net.tylermurphy.hideAndSeek.Main; import net.tylermurphy.hideAndSeek.game.listener.events.PlayerJumpEvent; @@ -22,6 +21,7 @@ public class MovementHandler implements Listener { @EventHandler(priority = EventPriority.HIGHEST) public void onMove(PlayerMoveEvent event) { + if (event.getTo() == null || event.getTo().getWorld() == null) return; checkJumping(event); checkBounds(event); 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..e29350b --- /dev/null +++ b/src/main/java/net/tylermurphy/hideAndSeek/game/util/Disguise.java @@ -0,0 +1,175 @@ +package net.tylermurphy.hideAndSeek.game.util; + +import net.tylermurphy.hideAndSeek.Main; +import net.tylermurphy.hideAndSeek.util.packet.BlockChangePacket; +import net.tylermurphy.hideAndSeek.util.packet.EntityTeleportPacket; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.*; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.scoreboard.Scoreboard; +import org.bukkit.scoreboard.Team; + +@SuppressWarnings("deprecation") +public class Disguise { + + final Player hider; + final Material material; + FallingBlock block; + Horse hitBox; + Location blockLocation; + boolean solid, solidify; + static Team hidden; + + static { + if(Main.getInstance().supports(9)) { + Scoreboard board = Bukkit.getScoreboardManager().getMainScoreboard(); + hidden = board.getTeam("KenshinHideAndSeek_CollisionGroup"); + if (hidden == null) { + hidden = board.registerNewTeam("KenshinHideAndSeek_CollisionGroup"); + } + hidden.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER); + hidden.setCanSeeFriendlyInvisibles(false); + } + } + + public Disguise(Player player, Material material){ + this.hider = player; + this.material = material; + this.solid = false; + respawnFallingBlock(); + player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 1000000, 0,false, false)); + if(Main.getInstance().supports(9)) { + hidden.addEntry(player.getName()); + } else { + hider.spigot().setCollidesWithEntities(false); + } + } + + public void remove(){ + if(block != null) + block.remove(); + if(hitBox != null){ + if(Main.getInstance().supports(9)) + hidden.removeEntry(hitBox.getUniqueId().toString()); + hitBox.remove(); + } + if(solid) + sendBlockUpdate(blockLocation, Material.AIR); + hider.removePotionEffect(PotionEffectType.INVISIBILITY); + if(Main.getInstance().supports(9)) { + hidden.removeEntry(hider.getName()); + } else { + hider.spigot().setCollidesWithEntities(true); + } + } + + public int getEntityID() { + if(block == null) return -1; + return block.getEntityId(); + } + + public int getHitBoxID() { + if(hitBox == null) return -1; + return hitBox.getEntityId(); + } + + public Player getPlayer() { + return hider; + } + + public void update(){ + + if(block == null || block.isDead()){ + if(block != null) block.remove(); + respawnFallingBlock(); + } + + if(solidify){ + if(!solid) { + solid = true; + blockLocation = hider.getLocation().getBlock().getLocation(); + respawnHitbox(); + } + sendBlockUpdate(blockLocation, material); + } else if(solid){ + solid = false; + if(Main.getInstance().supports(9)) + hidden.removeEntry(hitBox.getUniqueId().toString()); + hitBox.remove(); + hitBox = null; + sendBlockUpdate(blockLocation, Material.AIR); + } + toggleEntityVisibility(block, !solid); + teleportEntity(hitBox, true); + teleportEntity(block, solid); + } + + public void setSolidify(boolean value){ + this.solidify = value; + } + + private void sendBlockUpdate(Location location, Material material){ + BlockChangePacket packet = new BlockChangePacket(); + packet.setBlockPosition(location); + packet.setMaterial(material); + Bukkit.getOnlinePlayers().forEach(receiver -> { + if(receiver.getName().equals(hider.getName())) return; + packet.send(receiver); + }); + } + + private void teleportEntity(Entity entity, boolean center) { + if(entity == null) return; + EntityTeleportPacket packet = new EntityTeleportPacket(); + packet.setEntity(entity); + double x,y,z; + if(center){ + x = Math.round(hider.getLocation().getX()+.5)-.5; + y = Math.round(hider.getLocation().getY()); + z = Math.round(hider.getLocation().getZ()+.5)-.5; + } else { + x = hider.getLocation().getX(); + y = hider.getLocation().getY(); + z = hider.getLocation().getZ(); + } + packet.setX(x); + packet.setY(y); + packet.setZ(z); + Bukkit.getOnlinePlayers().forEach(packet::send); + } + + private void toggleEntityVisibility(Entity entity, boolean show){ + if(entity == null) return; + 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 respawnFallingBlock(){ + block = hider.getLocation().getWorld().spawnFallingBlock(hider.getLocation().add(0, 1000, 0), material, (byte)0); + block.setGravity(false); + block.setDropItem(false); + block.setInvulnerable(true); + } + + private void respawnHitbox(){ + hitBox = (Horse) hider.getLocation().getWorld().spawnEntity(hider.getLocation().add(0, 1000, 0), EntityType.HORSE); + hitBox.setAI(false); + hitBox.setGravity(false); + hitBox.setInvulnerable(true); + hitBox.setCanPickupItems(false); + hitBox.setCollidable(false); + hitBox.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 1000000, 0,false, false)); + if(Main.getInstance().supports(9)){ + hidden.addEntry(hitBox.getUniqueId().toString()); + } + } + +}
\ No newline at end of file |