diff --git a/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/BukkitFuture.java b/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/BukkitFuture.java new file mode 100644 index 000000000..e63644ac2 --- /dev/null +++ b/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/BukkitFuture.java @@ -0,0 +1,111 @@ +package mineplex.core.common.util; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Utilities for interleaving Bukkit scheduler operations as + * intermediate and terminal operations in a {@link CompletionStage} + * pipeline. + *

+ * Any {@link Function}s returned by methods are suitable for use + * in {@link CompletionStage#thenCompose(Function)} + * + * @see CompletableFuture#thenCompose(Function) + */ +public class BukkitFuture +{ + private static final Plugin LOADING_PLUGIN = JavaPlugin.getProvidingPlugin(BukkitFuture.class); + + private static void runBlocking(Runnable action) + { + Bukkit.getScheduler().runTask(LOADING_PLUGIN, action); + } + + /** + * Finalize a {@link CompletionStage} by consuming its value + * on the main thread. + * + * @param action the {@link Consumer} to call on the main thread + * @return a {@link Function} to be passed as an argument to + * {@link CompletionStage#thenCompose(Function)} + * @see CompletableFuture#thenCompose(Function) + */ + public static Function> accept(Consumer action) + { + return val -> + { + CompletableFuture future = new CompletableFuture<>(); + runBlocking(() -> + { + action.accept(val); + future.complete(null); + }); + return future; + }; + } + + /** + * Finalize a {@link CompletionStage} by executing code on the + * main thread after its completion. + * + * @param action the {@link Runnable} that will execute + * @return a {@link Function} to be passed as an argument to + * {@link CompletionStage#thenCompose(Function)} + * @see CompletableFuture#thenCompose(Function) + */ + public static Function> run(Runnable action) + { + return val -> + { + CompletableFuture future = new CompletableFuture<>(); + runBlocking(() -> + { + action.run(); + future.complete(null); + }); + return future; + }; + } + + /** + * Transform a value contained within a {@link CompletionStage} + * by executing a mapping {@link Function} on the main thread. + * + * @param fn the {@link Function} used to transform the value + * @return a {@link Function} to be passed as an argument to + * {@link CompletionStage#thenCompose(Function)} + * @see CompletableFuture#thenCompose(Function) + */ + public static Function> map(Function fn) + { + return val -> + { + CompletableFuture future = new CompletableFuture<>(); + runBlocking(() -> future.complete(fn.apply(val))); + return future; + }; + } + + /** + * Create a {@link CompletionStage} from a supplier executed on the + * main thread. + * + * @param supplier the supplier to run on the main thread + * @return a {@link CompletionStage} whose value will be supplied + * during the next Minecraft tick + */ + public static CompletionStage supply(Supplier supplier) + { + CompletableFuture future = new CompletableFuture<>(); + runBlocking(() -> future.complete(supplier.get())); + return future; + } +} diff --git a/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilWorld.java b/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilWorld.java index cef329774..fe44de9c8 100644 --- a/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilWorld.java +++ b/Plugins/Mineplex.Core.Common/src/mineplex/core/common/util/UtilWorld.java @@ -10,6 +10,8 @@ import org.bukkit.World.Environment; import org.bukkit.WorldBorder; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; +import org.bukkit.craftbukkit.v1_8_R3.entity.CraftEntity; +import org.bukkit.entity.Entity; import org.bukkit.util.Vector; import com.google.common.collect.Lists; @@ -28,7 +30,12 @@ public class UtilWorld if (chunk == null) return ""; - return chunk.getWorld().getName() + "," + chunk.getX() + "," + chunk.getZ(); + return chunkToStr(chunk.getWorld().getName(), chunk.getX(), chunk.getZ()); + } + + public static String chunkToStr(String world, int x, int z) + { + return world + "," + x + "," + z; } public static String chunkToStrClean(Chunk chunk) @@ -289,4 +296,15 @@ public class UtilWorld return startX >= minX && startZ <= maxX && endX >= minZ && endZ <= maxZ; } + public static double distanceSquared(Entity a, Entity b) + { + if (a.getWorld() != b.getWorld()) + throw new IllegalArgumentException("Different worlds: " + a.getWorld().getName() + " and " + b.getWorld().getName()); + net.minecraft.server.v1_8_R3.Entity entityA = ((CraftEntity) a).getHandle(); + net.minecraft.server.v1_8_R3.Entity entityB = ((CraftEntity) b).getHandle(); + double dx = entityA.locX - entityB.locX; + double dy = entityA.locY - entityB.locY; + double dz = entityA.locZ - entityB.locZ; + return (dx * dx) + (dy * dy) + (dz * dz); + } } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/account/CoreClientManager.java b/Plugins/Mineplex.Core/src/mineplex/core/account/CoreClientManager.java index 69a745818..f2c320b74 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/account/CoreClientManager.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/account/CoreClientManager.java @@ -491,7 +491,7 @@ public class CoreClientManager extends MiniPlugin } } - @EventHandler(priority = EventPriority.HIGHEST) + @EventHandler(priority = EventPriority.MONITOR) public void Quit(PlayerQuitEvent event) { // When an account is logged in to this server and the same account name logs in diff --git a/Plugins/Mineplex.Core/src/mineplex/core/creature/Creature.java b/Plugins/Mineplex.Core/src/mineplex/core/creature/Creature.java index f5bc05d1b..e892c9f4d 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/creature/Creature.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/creature/Creature.java @@ -103,6 +103,11 @@ public class Creature extends MiniPlugin event.setDroppedExp(0); List drops = event.getDrops(); + if (event.getEntity().hasMetadata("Creature.DoNotDrop")) + { + drops.clear(); + return; + } if (event.getEntityType() == EntityType.PLAYER) drops.add(ItemStackFactory.Instance.CreateStack(Material.BONE, 1)); diff --git a/Plugins/Mineplex.Core/src/mineplex/core/database/PlayerKeyValueRepository.java b/Plugins/Mineplex.Core/src/mineplex/core/database/PlayerKeyValueRepository.java new file mode 100644 index 000000000..e0148bd0a --- /dev/null +++ b/Plugins/Mineplex.Core/src/mineplex/core/database/PlayerKeyValueRepository.java @@ -0,0 +1,291 @@ +package mineplex.core.database; + +import com.google.common.collect.ImmutableMap; +import mineplex.serverdata.database.DBPool; + +import java.sql.*; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +/** + * A SQL-backed repository supporting {@link String} keys and + * values of type {@link V} + *

+ * Each java primitive (sans char) and String are supported by default. + * Serializing functions for any additional types can be supplied + * to {@link PlayerKeyValueRepository(String, Serializer, Deserializer)}. + * For example, if {@link String} was not supported, one could use: + *

+ * {@code new PlayerKeyValueRepository("tableName", PreparedStatement::setString, ResultSet::getString, "VARCHAR(255)")} + *

+ * NOTE: EACH CONSTRUCTOR IS BLOCKING, and initializes a backing table + * if one does not yet exist + * + * @param The value type to use for this repository + */ +public class PlayerKeyValueRepository +{ + private static final ImmutableMap, ValueMapper> PRIM_MAPPERS = ImmutableMap., ValueMapper>builder() + .put(String.class, new ValueMapper<>(PreparedStatement::setString, ResultSet::getString, "VARCHAR(255)")) + .put(Boolean.class, new ValueMapper<>(PreparedStatement::setBoolean, ResultSet::getBoolean, "BOOL")) + .put(Byte.class, new ValueMapper<>(PreparedStatement::setByte, ResultSet::getByte, "TINYINT")) + .put(Short.class, new ValueMapper<>(PreparedStatement::setShort, ResultSet::getShort, "SMALLINT")) + .put(Integer.class, new ValueMapper<>(PreparedStatement::setInt, ResultSet::getInt, "INTEGER")) + .put(Long.class, new ValueMapper<>(PreparedStatement::setLong, ResultSet::getLong, "BIGINT")) + .put(Float.class, new ValueMapper<>(PreparedStatement::setFloat, ResultSet::getFloat, "REAL")) + .put(Double.class, new ValueMapper<>(PreparedStatement::setDouble, ResultSet::getDouble, "DOUBLE")) + .build(); + private final String _tableName; + private final ValueMapper _mapper; + + /** + * Build a PlayerKeyValueRepository with the given class' + * built-in deserializer. + * + * @param tableName the underlying table's name + * @param clazz the type of values to used + * @throws IllegalArgumentException if the provided class isn't a supported type + */ + @SuppressWarnings("unchecked") // java's generics are garbage. + public PlayerKeyValueRepository(String tableName, Class clazz) // we could infer the type parameter at runtime, but it's super ugly + { + this(tableName, (ValueMapper) PRIM_MAPPERS.get(clazz)); + } + + /** + * Build a PlayerKeyValueRepository with an explicit deserializer. + * This is the constructor to use if the type you're deserializing + * isn't supported by default. + * + * @param tableName the underlying table's name + * @param serializer the serializing function used to insert values + * @param deserializer the deserializing function used to retrieve + * values + * @param columnDef the value type's SQL datatype declaration, e.g., {@code "VARCHAR(255)"} for Strings. + */ + public PlayerKeyValueRepository(String tableName, Serializer serializer, Deserializer deserializer, String columnDef) + { + this(tableName, new ValueMapper(serializer, deserializer, columnDef)); + } + + private PlayerKeyValueRepository(String tableName, ValueMapper mapper) + { + this._tableName = tableName; + this._mapper = mapper; + + // Create a table to back this repository + try (Connection conn = DBPool.getAccount().getConnection()) + { + Statement stmt = conn.createStatement(); + stmt.executeUpdate("CREATE TABLE IF NOT EXISTS " + _tableName + "(" + + "accountId INT NOT NULL," + + "kvKey VARCHAR(255) NOT NULL," + + "kvValue " + _mapper._columnDef + "," + + "PRIMARY KEY (accountId,kvKey)," + + "INDEX acc_ind (accountId)," + + "FOREIGN KEY (accountId) REFERENCES accounts(id) ON DELETE NO ACTION ON UPDATE NO ACTION" + + ")"); + } + catch (SQLException e) + { + e.printStackTrace(); + } + } + + /** + * Get all value for a player's key + * + * @param uuid the {@link UUID} of the player + * @return a CompletableFuture containing all key/value pairs + * associated with the player + */ + public CompletableFuture get(UUID uuid, String key) + { + return CompletableFuture.supplyAsync(() -> + { + try (Connection conn = DBPool.getAccount().getConnection()) + { + PreparedStatement stmt = conn.prepareStatement("SELECT kvValue FROM " + _tableName + " WHERE accountId = (SELECT id FROM accounts WHERE uuid=?) AND kvKey=?"); + stmt.setString(1, uuid.toString()); + stmt.setString(2, key); + + ResultSet set = stmt.executeQuery(); + if (set.next()) + { + return _mapper._deserializer.read(set, 1); + } + return null; + } catch (SQLException ignored) {} + + return null; // yuck + }); + } + + /** + * Get all key/value pairs for a player + * + * @param uuid the {@link UUID} of the player + * @return a CompletableFuture containing all key/value pairs + * associated with the player + */ + public CompletableFuture> getAll(UUID uuid) + { + return CompletableFuture.supplyAsync(() -> + { + try (Connection conn = DBPool.getAccount().getConnection()) + { + PreparedStatement stmt = conn.prepareStatement("SELECT kvKey, kvValue FROM " + _tableName + " WHERE accountId = (SELECT id FROM accounts WHERE uuid=?)"); + stmt.setString(1, uuid.toString()); + + ResultSet set = stmt.executeQuery(); + Map results = new HashMap<>(); + while (set.next()) + { + results.put(set.getString(1), _mapper._deserializer.read(set, 2)); + } + return results; + } catch (SQLException ignored) {} + + return new HashMap<>(); // yuck + }); + } + + /** + * Insert a key/value pair for a player + * + * @param uuid the {@link UUID} of the player + * @param key the key to insert + * @param value the value to insert + * @return a {@link CompletableFuture} whose value indicates + * success or failure + */ + public CompletableFuture put(UUID uuid, String key, V value) + { + return CompletableFuture.supplyAsync(() -> + { + try (Connection conn = DBPool.getAccount().getConnection()) + { + PreparedStatement stmt = conn.prepareStatement("REPLACE INTO " + _tableName + " (accountId, kvKey, kvValue) SELECT accounts.id, ?, ? FROM accounts WHERE uuid=?"); + stmt.setString(1, key); + _mapper._serializer.write(stmt, 2, value); + stmt.setString(3, uuid.toString()); + stmt.executeUpdate(); + return true; + + } catch (SQLException ignored) {} + + return false; + }); + } + + /** + * Insert many key/value pairs for a player + * + * @param uuid the {@link UUID} of the player + * @param values the map whose entries will be inserted for the + * player + * @return a {@link CompletableFuture} whose value indicates + * success or failure + */ + public CompletableFuture putAll(UUID uuid, Map values) + { + return CompletableFuture.supplyAsync(() -> + { + try (Connection conn = DBPool.getAccount().getConnection()) + { + PreparedStatement stmt = conn.prepareStatement("REPLACE INTO " + _tableName + " (accountId, kvKey, kvValue) SELECT accounts.id, ?, ? FROM accounts WHERE uuid=?"); + stmt.setString(3, uuid.toString()); + + for (Map.Entry entry : values.entrySet()) + { + stmt.setString(1, entry.getKey()); + _mapper._serializer.write(stmt, 2, entry.getValue()); + stmt.addBatch(); + } + stmt.executeBatch(); + return true; + + } catch (SQLException ignored) {} + + return false; + }); + } + + /** + * Remove a key's value for a player + * + * @param uuid the {@link UUID} of the player + * @param key the key to remove + * @return a {@link CompletableFuture} whose value indicates + * success or failure + */ + public CompletableFuture remove(UUID uuid, String key) + { + return CompletableFuture.supplyAsync(() -> + { + try (Connection conn = DBPool.getAccount().getConnection()) + { + PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + _tableName + " WHERE accountId=(SELECT id FROM accounts WHERE uuid=?) AND kvKey=?"); + stmt.setString(1, uuid.toString()); + stmt.setString(2, key); + stmt.executeUpdate(); + return true; + + } catch (SQLException ignored) {} + + return false; + }); + } + + /** + * Remove all key/value pairs for a player + * + * @param uuid the {@link UUID} of the player + * @return a {@link CompletableFuture} whose value indicates + * success or failure + */ + public CompletableFuture removeAll(UUID uuid) + { + return CompletableFuture.supplyAsync(() -> + { + try (Connection conn = DBPool.getAccount().getConnection()) + { + PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + _tableName + " WHERE accountId=(SELECT id FROM accounts WHERE uuid=?)"); + stmt.setString(1, uuid.toString()); + stmt.executeUpdate(); + return true; + + } catch (SQLException ignored) {} + + return false; + }); + } + + private static class ValueMapper + { + private final Serializer _serializer; + private final Deserializer _deserializer; + private final String _columnDef; + + private ValueMapper(Serializer serializer, Deserializer deserializer, String columnDef) + { + _serializer = serializer; + _deserializer = deserializer; + _columnDef = columnDef; + } + } + + @FunctionalInterface + public interface Serializer + { + void write(PreparedStatement statement, int index, V value) throws SQLException; + } + + @FunctionalInterface + public interface Deserializer + { + V read(ResultSet resultSet, int index) throws SQLException; + } +} diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/core/incognito/events/IncognitoHidePlayerEvent.java b/Plugins/Mineplex.Game.Clans/src/mineplex/core/incognito/events/IncognitoHidePlayerEvent.java deleted file mode 100644 index 54dabeebc..000000000 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/core/incognito/events/IncognitoHidePlayerEvent.java +++ /dev/null @@ -1,47 +0,0 @@ -package mineplex.core.incognito.events; - -import org.bukkit.entity.Player; -import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; - -/** - * Called when an Incognito player is getting hidden from all other players. - */ -public class IncognitoHidePlayerEvent extends Event -{ - private static final HandlerList handlers = new HandlerList(); - - private Player _player; - private boolean _cancelled; - - public IncognitoHidePlayerEvent(Player player) - { - _player = player; - } - - public Player getPlayer() - { - return _player; - } - - public void setCancelled(boolean cancelled) - { - _cancelled = cancelled; - } - - public boolean isCancelled() - { - return _cancelled; - } - - public HandlerList getHandlers() - { - return handlers; - } - - public static HandlerList getHandlerList() - { - return handlers; - } - -} \ No newline at end of file diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Farming.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Farming.java index f7145451e..1027a2253 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Farming.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/Farming.java @@ -1,73 +1,72 @@ package mineplex.game.clans; +import com.google.common.collect.Sets; import mineplex.core.MiniPlugin; +import mineplex.core.common.util.UtilItem; import mineplex.core.itemstack.ItemStackFactory; import mineplex.core.common.util.F; import mineplex.core.common.util.UtilPlayer; +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.event.EventHandler; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.plugin.java.JavaPlugin; +import java.util.Set; + public class Farming extends MiniPlugin { + private static final Set PLANTABLE = Sets.newHashSet( + Material.WHEAT, + Material.SUGAR_CANE_BLOCK, + Material.PUMPKIN_STEM, + Material.MELON_STEM, + Material.COCOA, + Material.CARROT, + Material.POTATO + ); + public Farming(JavaPlugin plugin) { super("Farming", plugin); } - @EventHandler + @EventHandler (ignoreCancelled = true) public void BlockBreak(BlockBreakEvent event) { - if (event.isCancelled()) - return; - if (event.getBlock().getType() != Material.LEAVES) return; - if (event.getPlayer().getItemInHand() != null) - if (event.getPlayer().getItemInHand().getType() == Material.SHEARS) - return; - + if (UtilItem.matchesMaterial(event.getPlayer().getItemInHand(), Material.SHEARS)) + return; + + Location dropLocation = event.getBlock().getLocation().add(0.5, 0.5, 0.5); + if (Math.random() > 0.9) - event.getBlock().getWorld().dropItemNaturally( - event.getBlock().getLocation().add(0.5, 0.5, 0.5), - ItemStackFactory.Instance.CreateStack(Material.APPLE)); + event.getBlock().getWorld().dropItemNaturally(dropLocation, ItemStackFactory.Instance.CreateStack(Material.APPLE)); if (Math.random() > 0.999) - event.getBlock().getWorld().dropItemNaturally( - event.getBlock().getLocation().add(0.5, 0.5, 0.5), - ItemStackFactory.Instance.CreateStack(Material.GOLDEN_APPLE)); + event.getBlock().getWorld().dropItemNaturally(dropLocation, ItemStackFactory.Instance.CreateStack(Material.GOLDEN_APPLE)); } - @SuppressWarnings("deprecation") - @EventHandler + @EventHandler (ignoreCancelled = true) public void BlockPlace(BlockPlaceEvent event) { - if (event.isCancelled()) + if (!PLANTABLE.contains(event.getBlock().getType())) return; - if ( - event.getBlock().getTypeId() != 59 && - event.getBlock().getTypeId() != 83 && - event.getBlock().getTypeId() != 104 && - event.getBlock().getTypeId() != 105 && - event.getBlock().getTypeId() != 127 && - event.getBlock().getTypeId() != 141 && - event.getBlock().getTypeId() != 142 - ) - return; + double blockY = event.getBlock().getLocation().getY(); + double seaLevel = event.getBlock().getWorld().getSeaLevel(); - if (event.getBlock().getLocation().getY() < event.getBlock().getWorld().getSeaLevel() - 12) + if (blockY < seaLevel - 12) { UtilPlayer.message(event.getPlayer(), F.main(getName(), "You cannot plant " + F.item(ItemStackFactory.Instance.GetName(event.getPlayer().getItemInHand(), true)) + " this deep underground.")); event.setCancelled(true); } - - else if (event.getBlock().getLocation().getY() > event.getBlock().getWorld().getSeaLevel() + 24) + else if (blockY > seaLevel + 24) { UtilPlayer.message(event.getPlayer(), F.main(getName(), "You cannot plant " + F.item(ItemStackFactory.Instance.GetName(event.getPlayer().getItemInHand(), true)) + " at this altitude.")); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansAdmin.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansAdmin.java index b7d073814..4f0017ea9 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansAdmin.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansAdmin.java @@ -114,7 +114,6 @@ public class ClansAdmin UtilPlayer.message(caller, F.help("/c x demote ", "Demote Player in Mimic", Rank.CMOD)); UtilPlayer.message(caller, F.help("/c x kick ", "Kick Player from Mimic", Rank.CMOD)); UtilPlayer.message(caller, F.help("/c x ally ", "Send Alliance to Mimic", Rank.CMOD)); - UtilPlayer.message(caller, F.help("/c x trust ", "Give Trust to Clan", Rank.CMOD)); UtilPlayer.message(caller, F.help("/c x neutral ", "Set Neutrality", Rank.CMOD)); UtilPlayer.message(caller, F.help("/c x enemy ", "Start Invasion", Rank.CMOD)); UtilPlayer.message(caller, F.help("/c x rename ", "Change the name of Mimic", Rank.CMOD)); @@ -379,12 +378,6 @@ public class ClansAdmin if (target == null) return; - if (target.getName().equals(caller.getName())) - { - UtilPlayer.message(caller, F.main("Clans Admin", "You cannot invite yourself.")); - return; - } - //Inform clan.inform(caller.getName() + " invited " + target.getName() + " to join Clan " + clan.getName() + ".", caller.getName()); UtilPlayer.message(caller, F.main("Clans Admin", "You invited " + target.getName() + " to join " + F.elem("Clan " + clan.getName()) + ".")); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansGame.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansGame.java index 00d3f6229..8247f16c0 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansGame.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansGame.java @@ -303,16 +303,40 @@ public class ClansGame extends MiniPlugin UtilPlayer.message(player, F.main("Clans", "You cannot place blocks in " + F.elem("Borderlands") + ".")); return; } - + + // Banners/String + if (player.getGameMode() != GameMode.CREATIVE && player.getItemInHand() != null) + { + if (player.getItemInHand().getType() == Material.BANNER || player.getItemInHand().getType() == Material.STRING) + { + Location destLocation = event.getClickedBlock().getRelative(event.getBlockFace()).getLocation(); + ClanTerritory territory = _clans.getClanUtility().getClaim(destLocation); + if (territory != null) + { + if (territory.Owner.equals("Shops") || territory.Owner.equals("Fields") || territory.Owner.equals("Spawn") || territory.Owner.equals("Borderlands")) { + // Disallow + event.setCancelled(true); + + // Inform + UtilPlayer.message(player, F.main("Clans", "You cannot place that in " + F.elem(_clans.getClanUtility().getOwnerStringRel(destLocation, player)) + ".")); + return; + } + } + } + } + ClanRelation access = _clans.getClanUtility().getAccess(player, loc); ClanInfo clan = _clans.getClan(player); + ClanInfo mimicClan = _clans.getClanAdmin().getMimic(player, false); ClanInfo blockClan = _clans.getClanUtility().getClaim(loc) == null ? null : _clans.getClan(_clans.getClanUtility().getClaim(loc).Owner); + if (blockClan.equals(mimicClan)) access = ClanRelation.SELF; // Doors, chests, & furnaces - if (blockClan != null && !blockClan.equals(clan) && (event.getAction() == Action.RIGHT_CLICK_BLOCK && (loc.getBlock().getType().name().contains("DOOR") || UtilItem.doesHaveGUI(loc.getBlock().getType())))) + if (blockClan != null && (!blockClan.equals(clan) && !blockClan.equals(mimicClan)) && (event.getAction() == Action.RIGHT_CLICK_BLOCK && (loc.getBlock().getType().name().contains("DOOR") || UtilItem.doesHaveGUI(loc.getBlock().getType())))) { UtilPlayer.message(player, F.main("Clans", "You are not allowed to use that here.")); event.setCancelled(true); + return; } // Hoe Return diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java index 6ee70c96e..69af91170 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansManager.java @@ -44,7 +44,7 @@ import mineplex.game.clans.clans.commands.*; import mineplex.game.clans.clans.data.PlayerClan; import mineplex.game.clans.clans.event.ClansPlayerDeathEvent; import mineplex.game.clans.clans.gui.ClanShop; -import mineplex.game.clans.clans.invsee.Invsee; +import mineplex.game.clans.clans.invsee.InvseeManager; import mineplex.game.clans.clans.loot.LootManager; import mineplex.game.clans.clans.map.ItemMapManager; import mineplex.game.clans.clans.nameblacklist.ClansBlacklist; @@ -260,7 +260,7 @@ public class ClansManager extends MiniClientPluginimplements IRelati new TntGeneratorManager(plugin, this); new SupplyDropManager(plugin, this); - new Invsee(this); + new InvseeManager(this); _explosion = new Explosion(plugin, blockRestore); _warPointEvasion = new WarPointEvasion(plugin); @@ -555,19 +555,6 @@ public class ClansManager extends MiniClientPluginimplements IRelati _classManager.GetRepository().SaveCustomBuild(activeBuild); } } - - @EventHandler - public void onPlayerKick(PlayerKickEvent event) - { - // Players using wind blade should not get kicked - if (event.getPlayer().getItemInHand() != null && event.getPlayer().getItemInHand().getItemMeta() != null && (C.cGold + "Wind Blade").equals(event.getPlayer().getItemInHand().getItemMeta().getDisplayName())) - { - if (event.getReason().contains("flying is not enabled")) - { - event.setCancelled(true); - } - } - } @EventHandler public void StaffIncognito(IncognitoStatusChangeEvent event) @@ -1175,7 +1162,7 @@ public class ClansManager extends MiniClientPluginimplements IRelati @EventHandler public void hubCommand(PlayerCommandPreprocessEvent event) { - if (event.getMessage().toLowerCase().startsWith("/lobby") || event.getMessage().toLowerCase().startsWith("/hub") || event.getMessage().toLowerCase().startsWith("/leave")) + if (event.getMessage().toLowerCase().equals("/lobby") || event.getMessage().toLowerCase().equals("/hub") || event.getMessage().toLowerCase().equals("/leave")) { Portal.getInstance().sendPlayerToServer(event.getPlayer(), "Lobby"); event.setCancelled(true); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansUtility.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansUtility.java index bb9567721..ff6c1fc70 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansUtility.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/ClansUtility.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; import org.bukkit.ChatColor; import org.bukkit.Chunk; @@ -28,6 +29,11 @@ import mineplex.game.clans.spawn.Spawn; public class ClansUtility { + // The maximum number of clans to search before exiting early. Inclusive + private static final int MAX_CLAN_SEARCH = 10; + // The maximum number of players to search before exiting early. Inclusive + private static final int MAX_PLAYER_SEARCH = 10; + private ClansManager _clansManager; public ClansUtility(ClansManager clans) @@ -113,55 +119,79 @@ public class ClansUtility public ClanInfo searchClanPlayer(Player caller, String name, boolean inform) { // CLAN - LinkedList clanMatchList = new LinkedList(); - + List clanMatchList = new ArrayList<>(MAX_CLAN_SEARCH); + for (ClanInfo cur : _clansManager.getClanMap().values()) { if (cur.getName().equalsIgnoreCase(name)) return cur; - + if (cur.getName().toLowerCase().contains(name.toLowerCase())) clanMatchList.add(cur); + + if (clanMatchList.size() > MAX_CLAN_SEARCH) break; } - + if (clanMatchList.size() == 1) return clanMatchList.get(0); - // No / Non-Unique - String clanMatchString = "None"; - if (clanMatchList.size() > 1) - { - for (ClanInfo cur : clanMatchList) - clanMatchString += cur.getName() + " "; - } - // PLAYER - LinkedList playerMatchList = new LinkedList(); - - for (ClanInfo clanInfo : _clansManager.getClanMap().values()) + List playerMatchList = new ArrayList<>(MAX_PLAYER_SEARCH); + + outer: for (ClanInfo clanInfo : _clansManager.getClanMap().values()) { for (ClansPlayer player : clanInfo.getMembers().values()) { if (player.getPlayerName().equalsIgnoreCase(name)) return clanInfo; - - if (player.getPlayerName().toLowerCase().contains(name.toLowerCase())) playerMatchList.add(clanInfo); - + + if (player.getPlayerName().toLowerCase().contains(name.toLowerCase())) + { + playerMatchList.add(clanInfo); + // No duplicate results please + continue outer; + } + + if (playerMatchList.size() > MAX_PLAYER_SEARCH) break outer; } } - + if (playerMatchList.size() == 1) return playerMatchList.get(0); - - // No / Non-Unique - String playerMatchString = "None"; - if (playerMatchList.size() > 1) - { - for (ClanInfo cur : playerMatchList) - playerMatchString += cur.getName() + " "; - } - + if (inform) { UtilPlayer.message(caller, F.main("Clan Search", "" + C.mCount + (clanMatchList.size() + playerMatchList.size()) + C.mBody + " matches for [" + C.mElem + name + C.mBody + "].")); - - UtilPlayer.message(caller, F.desc("Matches via Clan", clanMatchString)); - UtilPlayer.message(caller, F.desc("Matches via Player", playerMatchString)); + + if (clanMatchList.size() > MAX_CLAN_SEARCH) + { + UtilPlayer.message(caller, F.main("Clan Search", "Too many clans matched. Try a more specific search")); + } + else if (clanMatchList.size() == 0) + { + UtilPlayer.message(caller, F.main("Clan Search", "No clans matched. Try a more specific search")); + } + else + { + StringBuilder clanMatchString = new StringBuilder(); + for (ClanInfo clanInfo : clanMatchList) + { + clanMatchString.append(clanInfo.getName()).append(" "); + } + UtilPlayer.message(caller, F.desc("Matches via Clan", clanMatchString.toString())); + } + if (playerMatchList.size() > MAX_PLAYER_SEARCH) + { + UtilPlayer.message(caller, F.main("Clan Search", "Too many players matched. Try a more specific search")); + } + else if (playerMatchList.size() == 0) + { + UtilPlayer.message(caller, F.main("Clan Search", "No players matched. Try a more specific search")); + } + else + { + StringBuilder playerMatchString = new StringBuilder(); + for (ClanInfo clanInfo : playerMatchList) + { + playerMatchString.append(clanInfo.getName()).append(" "); + } + UtilPlayer.message(caller, F.desc("Matches via Player", playerMatchString.toString())); + } } return null; @@ -259,9 +289,15 @@ public class ClansUtility public boolean isSafe(Location loc) { - if (!_clansManager.getClaimMap().containsKey(UtilWorld.chunkToStr(loc.getChunk()))) return false; + // Fix for PC-279 + // Do not change to getChunk + // PlayerList#updatePlayers -> iterator -> PlayerVelocityEvent -> getChunk -> loadChunk -> loadPersistentEntities -> addTracker -> ITERATE ON SAME SET + int chunkX = loc.getBlockX() >> 4; + int chunkZ = loc.getBlockZ() >> 4; + String chunkStr = UtilWorld.chunkToStr(loc.getWorld().getName(), chunkX, chunkZ); + if (!_clansManager.getClaimMap().containsKey(chunkStr)) return false; - return _clansManager.getClaimMap().get(UtilWorld.chunkToStr(loc.getChunk())).isSafe(loc); + return _clansManager.getClaimMap().get(chunkStr).isSafe(loc); } public boolean isChunkHome(ClanInfo clan, Chunk chunk) diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/commands/ClansCommand.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/commands/ClansCommand.java index 741fc365a..059e55d54 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/commands/ClansCommand.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/commands/ClansCommand.java @@ -233,7 +233,6 @@ public class ClansCommand extends CommandBase UtilPlayer.message(caller, F.help("/c neutral ", "Request Neutrality with Clan", Rank.ADMIN)); UtilPlayer.message(caller, F.help("/c enemy ", "Declare ClanWar with Clan", Rank.ADMIN)); UtilPlayer.message(caller, F.help("/c ally ", "Send Alliance to Clan", Rank.ADMIN)); - UtilPlayer.message(caller, F.help("/c trust ", "Give Trust to Clan", Rank.ADMIN)); UtilPlayer.message(caller, F.help("/c claim", "Claim Territory", Rank.ADMIN)); UtilPlayer.message(caller, F.help("/c unclaim (all)", "Unclaim Territory", Rank.ADMIN)); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/gui/page/ClanMainPage.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/gui/page/ClanMainPage.java index a1cde46af..7db99c36b 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/gui/page/ClanMainPage.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/gui/page/ClanMainPage.java @@ -269,26 +269,33 @@ public class ClanMainPage extends ClanPageBase { String enemyName = clanWar.getClanA().equals(clan.getName()) ? clanWar.getClanB() : clanWar.getClanA(); final ClanInfo enemy = getPlugin().getClan(enemyName); - String itemName = enemyName; - Material material = USE_RESOURCE_ICONS ? ClanIcon.WAR.getMaterial() : Material.DIAMOND_SWORD; - byte data = USE_RESOURCE_ICONS ? ClanIcon.WAR.getData() : 0; - int warPoints = clanWar.getPoints(clan.getName()); - - ArrayList lore = new ArrayList(5); - lore.add(" "); - lore.add(C.Reset + C.cYellow + "War Points " + clan.getFormattedWarPoints(enemy)); - lore.add(" "); - lore.add(ChatColor.RESET + C.cGray + "Left Click " + C.cWhite + "Clan Info"); - - ShopItem shopItem = new ShopItem(material, data, itemName, lore.toArray(new String[0]), 0, false, false); - addButtonFakeCount(slot, shopItem, new IButton() + if (enemy != null) { - @Override - public void onClick(Player player, ClickType clickType) + String itemName = enemyName; + Material material = USE_RESOURCE_ICONS ? ClanIcon.WAR.getMaterial() : Material.DIAMOND_SWORD; + byte data = USE_RESOURCE_ICONS ? ClanIcon.WAR.getData() : 0; + int warPoints = clanWar.getPoints(clan.getName()); + + ArrayList lore = new ArrayList(5); + lore.add(" "); + lore.add(C.Reset + C.cYellow + "War Points " + clan.getFormattedWarPoints(enemy)); + lore.add(" "); + lore.add(ChatColor.RESET + C.cGray + "Left Click " + C.cWhite + "Clan Info"); + + ShopItem shopItem = new ShopItem(material, data, itemName, lore.toArray(new String[0]), 0, false, false); + addButtonFakeCount(slot, shopItem, new IButton() { - getShop().openPageForPlayer(player, new ClanWhoPage(getPlugin(), getShop(), getClientManager(), getDonationManager(), player, enemy, true)); - } - }, warPoints); + @Override + public void onClick(Player player, ClickType clickType) + { + getShop().openPageForPlayer(player, new ClanWhoPage(getPlugin(), getShop(), getClientManager(), getDonationManager(), player, enemy, true)); + } + }, warPoints); + } + else + { + System.err.println("Could not find enemy clan: " + enemyName); + } } private void addAllyButton(int slot, final ClanInfo ally) diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/invsee/Invsee.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/invsee/Invsee.java deleted file mode 100644 index 3409089ea..000000000 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/invsee/Invsee.java +++ /dev/null @@ -1,23 +0,0 @@ -package mineplex.game.clans.clans.invsee; - -import mineplex.core.MiniPlugin; -import mineplex.game.clans.clans.ClansManager; -import mineplex.game.clans.clans.invsee.commands.InvseeCommand; - -public class Invsee extends MiniPlugin -{ - private ClansManager _clansManager; - - public Invsee(ClansManager clansManager) - { - super("Inventory Viewer", clansManager.getPlugin()); - - _clansManager = clansManager; - } - - public void addCommands() - { - addCommand(new InvseeCommand(this)); - } - -} diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/invsee/InvseeManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/invsee/InvseeManager.java new file mode 100644 index 000000000..ab8af7853 --- /dev/null +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/invsee/InvseeManager.java @@ -0,0 +1,63 @@ +package mineplex.game.clans.clans.invsee; + +import mineplex.core.MiniPlugin; +import mineplex.core.common.util.UtilServer; +import mineplex.game.clans.clans.ClansManager; +import mineplex.game.clans.clans.invsee.commands.InvseeCommand; +import mineplex.game.clans.clans.invsee.ui.InvseeInventory; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class InvseeManager extends MiniPlugin +{ + private final Map _viewing = new HashMap<>(); + + public InvseeManager(ClansManager manager) + { + super("Invsee Manager", manager.getPlugin()); + } + + @Override + public void addCommands() + { + addCommand(new InvseeCommand(this)); + } + + public void doInvsee(OfflinePlayer target, Player requester) + { + InvseeInventory invseeInventory = _viewing.computeIfAbsent(target.getUniqueId(), key -> new InvseeInventory(this, target)); + invseeInventory.addAndShowViewer(requester); + } + + public boolean isBeingInvseen(OfflinePlayer player) + { + return _viewing.containsKey(player.getUniqueId()); + } + + public boolean isInvseeing(Player player) + { + for (InvseeInventory invseeInventory : _viewing.values()) + { + if (invseeInventory.isViewer(player)) + { + return true; + } + } + return false; + } + + public void close(UUID target) + { + InvseeInventory invseeInventory = _viewing.remove(target); + if (invseeInventory == null) + { + log("Expected non-null inventory when closing " + target); + return; + } + UtilServer.Unregister(invseeInventory); + } +} diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/invsee/commands/InvseeCommand.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/invsee/commands/InvseeCommand.java index f26393708..00f0e24b8 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/invsee/commands/InvseeCommand.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/invsee/commands/InvseeCommand.java @@ -1,50 +1,94 @@ package mineplex.game.clans.clans.invsee.commands; +import com.mojang.authlib.GameProfile; import mineplex.core.command.CommandBase; import mineplex.core.common.Rank; import mineplex.core.common.util.F; import mineplex.core.common.util.UtilPlayer; -import mineplex.game.clans.clans.invsee.Invsee; -import mineplex.game.clans.clans.invsee.ui.InvseeInventory; +import mineplex.game.clans.clans.invsee.InvseeManager; +import net.minecraft.server.v1_8_R3.MinecraftServer; +import net.minecraft.server.v1_8_R3.NBTTagCompound; +import net.minecraft.server.v1_8_R3.WorldNBTStorage; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; -public class InvseeCommand extends CommandBase +import java.util.UUID; + +public class InvseeCommand extends CommandBase { - public InvseeCommand(Invsee plugin) + public InvseeCommand(InvseeManager plugin) { super(plugin, Rank.ADMIN, "invsee"); } - @SuppressWarnings("deprecation") @Override public void Execute(Player caller, String[] args) { if (args.length == 0) { - UtilPlayer.message(caller, F.help("/invsee ", "View a player's inventory", Rank.ADMIN)); + UtilPlayer.message(caller, F.help("/invsee ", "View a player's inventory", Rank.ADMIN)); + return; } - else + UUID uuid = null; + try { - String name = args[0]; + uuid = UUID.fromString(args[0]); + } + catch (IllegalArgumentException failed) + { + } - OfflinePlayer player = Bukkit.getServer().getPlayer(name); - - if (player == null) + OfflinePlayer exactPlayer = Bukkit.getServer().getPlayerExact(args[0]); + if (exactPlayer == null) + { + if (uuid == null) { - player = Bukkit.getServer().getOfflinePlayer(name); + // We don't want to open the wrong OfflinePlayer's inventory, so if we can't fetch the UUID then abort + GameProfile gameProfile = MinecraftServer.getServer().getUserCache().getProfile(args[0]); + if (gameProfile == null) + { + UtilPlayer.message(caller, F.main("Invsee", "Player is offline and we could not find the UUID. Aborting")); + return; + } + uuid = gameProfile.getId(); } - - if (player == null) + if (uuid == null) { - UtilPlayer.message(caller, F.main("Clans", "Specified player is neither online nor offline. Perhaps they changed their name?")); + UtilPlayer.message(caller, F.main("Invsee", "Something has gone very wrong. Please report the username/uuid you tried to look up")); return; } - - new InvseeInventory(player).ShowTo(caller); + // We need to check if we actually have data on this player + // fixme main thread file IO but it's what the server does...? + NBTTagCompound compound = ((WorldNBTStorage) MinecraftServer.getServer().worlds.get(0).getDataManager()).getPlayerData(uuid.toString()); + if (compound == null) + { + UtilPlayer.message(caller, F.main("Invsee", "The player exists, but has never joined this server. No inventory to show")); + return; + } + exactPlayer = Bukkit.getServer().getOfflinePlayer(uuid); } + if (exactPlayer == null) + { + UtilPlayer.message(caller, F.main("Invsee", "Could not load offline player data. Does the player exist?")); + return; + } + if (exactPlayer.getUniqueId().equals(caller.getUniqueId())) + { + UtilPlayer.message(caller, F.main("Invsee", "You cannot invsee yourself!")); + return; + } + if (Plugin.isBeingInvseen(caller)) + { + UtilPlayer.message(caller, F.main("Invsee", "You cannot use invsee right now. Someone is invseeing you!")); + return; + } + if (exactPlayer.isOnline() && Plugin.isInvseeing((Player) exactPlayer)) + { + UtilPlayer.message(caller, F.main("Invsee", "You cannot use invsee right now. That person is currently using invsee!")); + return; + } + + Plugin.doInvsee(exactPlayer, caller); } - - } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/invsee/ui/InvseeInventory.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/invsee/ui/InvseeInventory.java index eb605e17b..69283e71a 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/invsee/ui/InvseeInventory.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/invsee/ui/InvseeInventory.java @@ -1,99 +1,407 @@ package mineplex.game.clans.clans.invsee.ui; +import mineplex.core.common.util.*; +import mineplex.core.itemstack.ItemStackFactory; +import mineplex.game.clans.clans.ClansManager; +import mineplex.game.clans.clans.invsee.InvseeManager; +import net.minecraft.server.v1_8_R3.*; +import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.OfflinePlayer; +import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftInventory; 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; +import org.bukkit.event.inventory.*; +import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; -import mineplex.core.common.util.C; -import mineplex.core.common.util.UtilCollections; -import mineplex.core.common.util.UtilServer; -import mineplex.core.updater.UpdateType; -import mineplex.core.updater.event.UpdateEvent; +import java.io.File; +import java.io.FileOutputStream; +import java.util.*; public class InvseeInventory implements Listener { - private OfflinePlayer _player; + private final InvseeManager _invseeManager; + + // This is the UUID of the target player. This should stay constant. Use this for comparison + private final UUID _uuid; + // This is the current player. It will switch when the player joins/quits + private OfflinePlayer _targetPlayer; + // This is the inventory of said player. If the player is not online then this inventory is a fake PlayerInventory + private net.minecraft.server.v1_8_R3.PlayerInventory _playerInventory; + // This is the set of all admins viewing this player + private Set _viewers = new HashSet<>(); + // This is the inventory that all admins will be looking at private Inventory _inventory; - - private boolean _online; - - private Player _admin; - - public InvseeInventory(OfflinePlayer player) + // This is whether the target player should be allowed to open any inventories + private boolean _canOpenInventory = true; + + public InvseeInventory(InvseeManager manager, OfflinePlayer player) { - _online = (_player = player) instanceof Player; - - _inventory = UtilServer.getServer().createInventory(null, 54, player.getName() + " " + (_online ? C.cGreen + "ONLINE" : C.cRed + "OFFLINE")); - + _invseeManager = manager; + _uuid = player.getUniqueId(); + _targetPlayer = player; + + _inventory = UtilServer.getServer().createInventory(null, 6 * 9, player.getName()); + + for (int index = 38; index < 45; index++) + { + _inventory.setItem(index, ItemStackFactory.Instance.CreateStack(Material.STAINED_GLASS_PANE, (byte) 14, 1, C.Bold)); + } + _inventory.setItem(47, ItemStackFactory.Instance.CreateStack(Material.STONE_BUTTON, (byte) 0, 1, C.cGreenB + "Inventory Control", generateLore(_canOpenInventory))); + + _inventory.setItem(48, ItemStackFactory.Instance.CreateStack(Material.STAINED_GLASS_PANE, (byte) 14, 1, C.Bold)); + UtilServer.RegisterEvents(this); + updateInventory(); + update(true); } - - public void ShowTo(Player admin) + + private String[] generateLore(boolean canOpenInventory) { - _admin = admin; - admin.openInventory(_inventory); + return UtilText.splitLinesToArray(new String[]{ + C.cYellow + "Left-Click" + C.cWhite + " to force close any open inventories the target has open", + "", + C.cYellow + "Right-Click" + C.cWhite + " to " + (canOpenInventory ? "disable inventory opening" : "enable inventory opening") + }, LineFormat.LORE); } - - @EventHandler - public void quit(PlayerQuitEvent event) + + /* + * Add the player to the list of viewers and open the inventory for him + */ + public void addAndShowViewer(Player requester) { - if (_online && event.getPlayer().equals(_player)) + _viewers.add(requester); + requester.openInventory(_inventory); + } + + /* + * Check whether the given player is currently viewing this InvseeInventory + */ + public boolean isViewer(Player check) + { + return _viewers.contains(check); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerJoinEvent event) + { + if (_uuid.equals(event.getPlayer().getUniqueId())) { - _admin.closeInventory(); + _targetPlayer = event.getPlayer(); + updateInventory(); } } - - @EventHandler - public void update(UpdateEvent event) + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(PlayerQuitEvent event) { - if (event.getType() != UpdateType.TICK) + // If a viewer quit, this will clean it up + _viewers.remove(event.getPlayer()); + if (_viewers.size() == 0) { - return; + _invseeManager.close(_uuid); } - - if (_online) + else { - if (!UtilCollections.equal(_inventory.getContents(), ((Player) _player).getInventory().getContents())) + // This should always work + _targetPlayer = Bukkit.getOfflinePlayer(_uuid); + updateInventory(); + } + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void on(InventoryOpenEvent event) + { + if (event.getPlayer().getUniqueId().equals(_targetPlayer.getUniqueId()) && !_canOpenInventory) + { + event.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(InventoryCloseEvent event) + { + if (_inventory.equals(event.getInventory())) + { + _viewers.remove(event.getPlayer()); + if (_viewers.size() == 0) { - _inventory.setContents(((Player) _player).getInventory().getContents()); - } - } - } - - @EventHandler - public void inventoryClick(InventoryClickEvent event) - { - if (event.getClickedInventory().equals(((Player) _player).getInventory())) - { - _inventory.setContents(((Player) _player).getInventory().getContents()); - } - else if (event.getClickedInventory().equals(_inventory)) - { - if (_online) - { - ((Player) _player).getInventory().setContents(_inventory.getContents()); - } - } - } - - @EventHandler - public void closeInventory(InventoryCloseEvent event) - { - if (event.getInventory().equals(_inventory)) - { - UtilServer.Unregister(this); - - if (!_online) - { - // save offline inv + _invseeManager.close(_uuid); } } } + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void on(InventoryDragEvent event) + { + if (_inventory.equals(event.getInventory())) + { + ClansManager.getInstance().runSync(() -> + { + update(false); + saveInventory(); + }); + } + else if (event.getWhoClicked().getUniqueId().equals(_targetPlayer.getUniqueId())) + { + ClansManager.getInstance().runSync(() -> + { + update(true); + saveInventory(); + }); + } + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void on(InventoryClickEvent event) + { + if (_inventory.equals(event.getClickedInventory())) + { + if (event.getCurrentItem() != null && event.getCurrentItem().getType() == Material.STAINED_GLASS_PANE + && event.getCurrentItem().getItemMeta() != null + && event.getCurrentItem().getItemMeta().getDisplayName() != null + && event.getCurrentItem().getItemMeta().getDisplayName().equals(C.Bold)) + { + event.setCancelled(true); + return; + } + if (event.getCurrentItem() != null && event.getCurrentItem().getType() == Material.STONE_BUTTON + && event.getCurrentItem().getItemMeta() != null + && event.getCurrentItem().getItemMeta().getDisplayName() != null + && event.getCurrentItem().getItemMeta().getDisplayName().equals(C.cGreenB + "Inventory Control")) + { + event.setCancelled(true); + if (event.getClick() == ClickType.LEFT) + { + if (_targetPlayer.isOnline()) + { + ((Player) _targetPlayer).closeInventory(); + } + } + else if (event.getClick() == ClickType.RIGHT) + { + if (_targetPlayer.isOnline()) + { + _canOpenInventory = !_canOpenInventory; + ItemMeta meta = event.getCurrentItem().getItemMeta(); + meta.setLore(Arrays.asList(generateLore(_canOpenInventory))); + event.getCurrentItem().setItemMeta(meta); + if (!_canOpenInventory) + { + ((Player) _targetPlayer).closeInventory(); + } + } + } + return; + } + + if (MAPPING_INVENTORY_REVERSE.containsKey(event.getRawSlot())) + { + ClansManager.getInstance().runSync(() -> + { + update(false); + saveInventory(); + }); + } + else if (MAPPING_CRAFTING_REVERSE.containsKey(event.getRawSlot())) + { + if (_targetPlayer.isOnline()) + { + ClansManager.getInstance().runSync(() -> + { + update(false); + }); + } + } + else if (event.getRawSlot() == 49) + { + if (_targetPlayer.isOnline()) + { + ClansManager.getInstance().runSync(() -> + { + update(false); + }); + } + } + } + else + { + if (event.getWhoClicked().getUniqueId().equals(_targetPlayer.getUniqueId())) + { + ClansManager.getInstance().runSync(() -> + { + update(true); + }); + } + } + } + + /* + * Updates the inventory instance + */ + private void updateInventory() + { + if (_targetPlayer.isOnline()) + { + _playerInventory = ((CraftPlayer) _targetPlayer).getHandle().inventory; + } + else + { + NBTTagCompound compound = ((WorldNBTStorage) MinecraftServer.getServer().worlds.get(0).getDataManager()).getPlayerData(_uuid.toString()); + // Should not matter if null + _playerInventory = new PlayerInventory(null); + if (compound.hasKeyOfType("Inventory", 9)) + { + _playerInventory.b(compound.getList("Inventory", 10)); + } + } + } + + private void saveInventory() + { + if (!_targetPlayer.isOnline()) + { + try + { + WorldNBTStorage worldNBTStorage = ((WorldNBTStorage) MinecraftServer.getServer().worlds.get(0).getDataManager()); + NBTTagCompound compound = worldNBTStorage.getPlayerData(_uuid.toString()); + compound.set("Inventory", new NBTTagList()); + _playerInventory.a(compound.getList("Inventory", 10)); + File file = new File(worldNBTStorage.getPlayerDir(), _targetPlayer.getUniqueId().toString() + ".dat.tmp"); + File file1 = new File(worldNBTStorage.getPlayerDir(), _targetPlayer.getUniqueId().toString() + ".dat"); + NBTCompressedStreamTools.a(compound, new FileOutputStream(file)); + if (file1.exists()) + { + file1.delete(); + } + + file.renameTo(file1); + } + catch (Exception var5) + { + _invseeManager.log("Failed to save player inventory for " + _targetPlayer.getName()); + for (Player player : _viewers) + { + UtilPlayer.message(player, F.main("Invsee", "Could not save inventory for " + _targetPlayer.getName())); + } + var5.printStackTrace(System.out); + } + } + } + + /* + * Update the player inventory and invsee inventory. + * + * @param targetClick If true, then it means the player being invseen has modified their inventory. Otherwise, it's the admin who has modified something + */ + private void update(boolean targetClick) + { + IInventory iInventoryThis = ((CraftInventory) _inventory).getInventory(); + + if (targetClick) + { + // Update items on hotbar + for (int otherSlot = 0; otherSlot < 9; otherSlot++) + { + iInventoryThis.setItem(MAPPING_INVENTORY.get(otherSlot), _playerInventory.getItem(otherSlot)); + } + // Update main inventory + for (int otherSlot = 9; otherSlot < 36; otherSlot++) + { + iInventoryThis.setItem(MAPPING_INVENTORY.get(otherSlot), _playerInventory.getItem(otherSlot)); + } + // Update armor + for (int otherSlot = 36; otherSlot < 40; otherSlot++) + { + iInventoryThis.setItem(MAPPING_INVENTORY.get(otherSlot), _playerInventory.getItem(otherSlot)); + } + + if (_targetPlayer.isOnline()) + { + ContainerPlayer containerPlayer = (ContainerPlayer) ((CraftPlayer) _targetPlayer).getHandle().defaultContainer; + for (int craftingIndex = 0; craftingIndex < 4; craftingIndex++) + { + iInventoryThis.setItem(MAPPING_CRAFTING.get(craftingIndex), containerPlayer.craftInventory.getItem(craftingIndex)); + } + } + iInventoryThis.setItem(49, _playerInventory.getCarried()); + } + else + { + // Update items on hotbar + for (int otherSlot = 0; otherSlot < 9; otherSlot++) + { + _playerInventory.setItem(otherSlot, iInventoryThis.getItem(MAPPING_INVENTORY.get(otherSlot))); + } + // Update main inventory + for (int otherSlot = 9; otherSlot < 36; otherSlot++) + { + _playerInventory.setItem(otherSlot, iInventoryThis.getItem(MAPPING_INVENTORY.get(otherSlot))); + } + // Update armor + for (int otherSlot = 36; otherSlot < 40; otherSlot++) + { + _playerInventory.setItem(otherSlot, iInventoryThis.getItem(MAPPING_INVENTORY.get(otherSlot))); + } + + if (_targetPlayer.isOnline()) + { + ContainerPlayer containerPlayer = (ContainerPlayer) ((CraftPlayer) _targetPlayer).getHandle().defaultContainer; + for (int craftingIndex = 0; craftingIndex < 4; craftingIndex++) + { + containerPlayer.craftInventory.setItem(craftingIndex, iInventoryThis.getItem(MAPPING_CRAFTING.get(craftingIndex))); + } + } + _playerInventory.setCarried(iInventoryThis.getItem(49)); + } + for (Player viewing : _viewers) + { + viewing.updateInventory(); + } + if (_targetPlayer.isOnline()) + { + ((Player) _targetPlayer).updateInventory(); + } + } + + // Maps slot indices of player inventories to slot indices of double chests + private static final Map MAPPING_INVENTORY = new HashMap<>(); + private static final Map MAPPING_INVENTORY_REVERSE = new HashMap<>(); + // Maps slot indices of player inventories to slot indices of crafting window + private static final Map MAPPING_CRAFTING = new HashMap<>(); + private static final Map MAPPING_CRAFTING_REVERSE = new HashMap<>(); + + static + { + int[] inventoryMapping = new int[] + { + 27, 28, 29, 30, 31, 32, 33, 34, 35, //Hotbar + 0, 1, 2, 3, 4, 5, 6, 7, 8, // Top row inventory + 9, 10, 11, 12, 13, 14, 15, 16, 17, //Second row inventory + 18, 19, 20, 21, 22, 23, 24, 25, 26, //Third row inventory + 53, 52, 51, 50 //Armor + }; + int[] craftingMapping = new int[] + { + 36, 37, //Top crafting + 45, 46 //Bottom crafting + }; + for (int i = 0; i < inventoryMapping.length; i++) + { + MAPPING_INVENTORY.put(i, inventoryMapping[i]); + MAPPING_INVENTORY_REVERSE.put(inventoryMapping[i], i); + } + + for (int i = 0; i < craftingMapping.length; i++) + { + MAPPING_CRAFTING.put(i, craftingMapping[i]); + MAPPING_CRAFTING_REVERSE.put(craftingMapping[i], i); + } + } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/map/ItemMapManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/map/ItemMapManager.java index f73eb2280..04e2fb410 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/map/ItemMapManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/map/ItemMapManager.java @@ -1,30 +1,26 @@ package mineplex.game.clans.clans.map; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.PrintWriter; +import java.io.*; import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; +import java.util.*; import java.util.Map.Entry; +import mineplex.core.common.util.*; import mineplex.game.clans.tutorial.TutorialManager; -import mineplex.game.clans.tutorial.map.TutorialMapManager; +import net.minecraft.server.v1_8_R3.*; import org.apache.commons.io.FileUtils; import org.bukkit.Bukkit; -import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_8_R3.CraftChunk; import org.bukkit.craftbukkit.v1_8_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_8_R3.util.LongHash; +import org.bukkit.craftbukkit.v1_8_R3.util.LongObjectHashMap; import org.bukkit.entity.ItemFrame; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.block.Action; import org.bukkit.event.entity.ItemSpawnEvent; import org.bukkit.event.entity.PlayerDeathEvent; @@ -35,6 +31,8 @@ import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.map.MapRenderer; @@ -46,13 +44,6 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Multisets; import mineplex.core.MiniPlugin; -import mineplex.core.common.util.C; -import mineplex.core.common.util.F; -import mineplex.core.common.util.UtilInv; -import mineplex.core.common.util.UtilPlayer; -import mineplex.core.common.util.UtilServer; -import mineplex.core.common.util.UtilTextBottom; -import mineplex.core.common.util.UtilTime; import mineplex.core.common.util.UtilTime.TimeUnit; import mineplex.core.itemstack.ItemBuilder; import mineplex.core.portal.ServerTransferEvent; @@ -62,82 +53,119 @@ import mineplex.game.clans.clans.ClansManager; import mineplex.game.clans.clans.ClansUtility; import mineplex.game.clans.clans.map.events.PlayerGetMapEvent; import mineplex.game.clans.clans.worldevent.WorldEventManager; -import net.minecraft.server.v1_8_R3.Block; -import net.minecraft.server.v1_8_R3.BlockPosition; -import net.minecraft.server.v1_8_R3.Blocks; -import net.minecraft.server.v1_8_R3.IBlockData; -import net.minecraft.server.v1_8_R3.MaterialMapColor; -import net.minecraft.server.v1_8_R3.PersistentCollection; +/* + * This class manages what the Clans map will show. + * It will scan all relevant chunks (eg players nearby) every ticks + */ public class ItemMapManager extends MiniPlugin { - private int _blocksScan = 16 * 3; + // Every BLOCK_SCAN_INTERVAL we add as a new region to scan + private static final int BLOCK_SCAN_INTERVAL = 16 * 3; + // 1536 is the width of the entire world from one borderland to the other + private static final int HALF_WORLD_SIZE = 1536 / 2; + // This slot is where the Clans Map will go by default + private static final int CLANS_MAP_SLOT = 8; + + private static final String[] ZOOM_INFO; + + static + { + ZOOM_INFO = new String[4]; + for (int zoomLevel = 0; zoomLevel <= 3; zoomLevel++) + { + StringBuilder progressBar = new StringBuilder(C.cBlue); + + boolean colorChange = false; + for (int i = 2; i >= 0; i--) + { + if (!colorChange && i < zoomLevel) + { + progressBar.append(C.cGray); + colorChange = true; + } + char c; + switch (i) + { + case 0: + c = 'â–ˆ'; + break; + case 1: + c = 'â–†'; + break; + default: + c = 'â–„'; + break; + } + for (int a = 0; a < 4; a++) + { + progressBar.append(c); + } + + if (i > 0) + { + progressBar.append(" "); + } + } + ZOOM_INFO[zoomLevel] = progressBar.toString(); + } + } + private ClansUtility _clansUtility; private Comparator> _comparator; - private int _halfMapSize = 1536 / 2; - private int[][] _heightMap = new int[(_halfMapSize * 2) + 16][]; - private boolean _loadWorld = true; + private int[][] _heightMap = new int[(HALF_WORLD_SIZE * 2) + 16][]; private HashMap _map = new HashMap(); private short _mapId = -1; private HashMap _mapInfo = new HashMap(); private HashMap _scale = new HashMap(); - private ArrayList> _scanList = new ArrayList>(); + // Use LinkedList because operations are either add(Entry) which is O(1) and remove(0) which is O(1) on LinkedList but O(n) on ArrayList + private LinkedList> _scanList = new LinkedList>(); private World _world; + private WorldServer _nmsWorld; + private ChunkProviderServer _chunkProviderServer; + private ChunkRegionLoader _chunkRegionLoader; private WorldEventManager _eventManager; private TutorialManager _tutorial; public ItemMapManager(ClansManager clansManager, TutorialManager tutorial, WorldEventManager eventManager) { super("ItemMapManager", clansManager.getPlugin()); - + _clansUtility = clansManager.getClanUtility(); _eventManager = eventManager; _tutorial = tutorial; - _comparator = new Comparator>() + _comparator = (o1, o2) -> { + // Render the places outside the map first to speed up visual errors fixing + int outsideMap = Boolean.compare(o1.getValue() < -HALF_WORLD_SIZE, o2.getValue() < -HALF_WORLD_SIZE); - @Override - public int compare(Entry o1, Entry o2) + if (outsideMap != 0) { - // Render the places outside the map first to speed up visual errors fixing - int outsideMap = Boolean.compare(o1.getValue() < -_halfMapSize, o2.getValue() < -_halfMapSize); + return -outsideMap; + } - if (outsideMap != 0) - { - return -outsideMap; - } + double dist1 = 0; + double dist2 = 0; - double dist1 = 0; - double dist2 = 0; - - for (Player player : UtilServer.getPlayers()) - { - dist1 += getDistance(o1, player.getLocation().getX(), player.getLocation().getZ()); - dist2 += getDistance(o2, player.getLocation().getX(), player.getLocation().getZ()); - } - - if (dist1 != dist2) - { - return Double.compare(dist1, dist2); - } - - dist1 = getDistance(o1, 0, 0); - dist2 = getDistance(o2, 0, 0); + for (Player player : UtilServer.getPlayers()) + { + dist1 += getDistance(o1, player.getLocation().getX(), player.getLocation().getZ()); + dist2 += getDistance(o2, player.getLocation().getX(), player.getLocation().getZ()); + } + if (dist1 != dist2) + { return Double.compare(dist1, dist2); - } + + dist1 = getDistance(o1, 0, 0); + dist2 = getDistance(o2, 0, 0); + + return Double.compare(dist1, dist2); + }; - for (int x = -_halfMapSize; x < _halfMapSize; x += _blocksScan) - { - for (int z = -_halfMapSize - 16; z < _halfMapSize; z += (z < -_halfMapSize ? 16 : _blocksScan)) - { - _scanList.add(new HashMap.SimpleEntry(x, z)); - } - } - _scale.put(0, 1); // _scale.put(1, 2); _scale.put(1, 4); @@ -147,7 +175,7 @@ public class ItemMapManager extends MiniPlugin for (Entry entry : _scale.entrySet()) { - int size = (_halfMapSize * 2) / entry.getValue(); + int size = (HALF_WORLD_SIZE * 2) / entry.getValue(); Byte[][] bytes = new Byte[size][]; for (int i = 0; i < size; i++) @@ -165,6 +193,23 @@ public class ItemMapManager extends MiniPlugin _world = Bukkit.getWorld("world"); + try + { + Field chunkLoader = ChunkProviderServer.class.getDeclaredField("chunkLoader"); + chunkLoader.setAccessible(true); + _nmsWorld = ((CraftWorld) _world).getHandle(); + _chunkProviderServer = _nmsWorld.chunkProviderServer; + _chunkRegionLoader = (ChunkRegionLoader) chunkLoader.get(_chunkProviderServer); + if (_chunkRegionLoader == null) + { + throw new RuntimeException("Did not expect null chunkLoader"); + } + } + catch (ReflectiveOperationException e) + { + throw new RuntimeException("Could not reflectively access ChunkRegionLoader", e); + } + try { File file = new File("world/clans_map_id"); @@ -238,6 +283,58 @@ public class ItemMapManager extends MiniPlugin } rebuildScan(); + initialScan(); + } + + private void initialScan() + { + System.out.println("Beginning initial scan. There are " + _scanList.size() + " regions to scan"); + + // How many regions before logging an update (Currently set to every 20%) + int logPer = _scanList.size() / 5; + + while (!_scanList.isEmpty()) + { + Entry entry = _scanList.remove(0); + if (_scanList.size() % logPer == 0) + { + System.out.println("Running initial render... " + _scanList.size() + " sections to go"); + } + + int startingX = entry.getKey(); + int startingZ = entry.getValue(); + + boolean outsideMap = startingZ < -HALF_WORLD_SIZE; + + scanWorldMap(startingX, startingZ, !outsideMap, true); + + if (outsideMap) + { + continue; + } + + for (int scale = 1; scale < _scale.size(); scale++) + { + if (scale == 3) + continue; + + drawWorldScale(scale, startingX, startingZ); + colorWorldHeight(scale, startingX, startingZ); + } + + colorWorldHeight(0, startingX, startingZ); + } + + for (int x = -HALF_WORLD_SIZE; x < HALF_WORLD_SIZE; x += BLOCK_SCAN_INTERVAL) + { + for (int z = -HALF_WORLD_SIZE; z < HALF_WORLD_SIZE; z += BLOCK_SCAN_INTERVAL) + { + drawWorldScale(3, x, z); + colorWorldHeight(3, x, z); + } + } + + System.out.println("Finished first map scan and render"); } private void setupRenderer(MapView view) @@ -256,10 +353,7 @@ public class ItemMapManager extends MiniPlugin if (!(event.getRightClicked() instanceof ItemFrame)) return; - ItemStack item = event.getPlayer().getItemInHand(); - - if (item == null || item.getType() != Material.MAP || item.getDurability() < _mapId - || item.getDurability() > _mapId + 100) + if (!isItemClansMap(event.getPlayer().getItemInHand())) return; event.setCancelled(true); @@ -270,7 +364,7 @@ public class ItemMapManager extends MiniPlugin */ public int calcMapCenter(int zoom, int cord) { - int mapSize = _halfMapSize / zoom; // This is how large the map is in pixels + int mapSize = HALF_WORLD_SIZE / zoom; // This is how large the map is in pixels int mapCord = cord / zoom; // This is pixels from true center of map, not held map @@ -309,7 +403,7 @@ public class ItemMapManager extends MiniPlugin Byte[][] map = _map.get(scale); int zoom = getZoom(scale); - for (int x = startingX; x < startingX + _blocksScan; x += zoom) + for (int x = startingX; x < startingX + BLOCK_SCAN_INTERVAL; x += zoom) { double d0 = 0; @@ -319,10 +413,10 @@ public class ItemMapManager extends MiniPlugin { for (int addZ = 0; addZ < zoom; addZ++) { - int hX = x + addX + _halfMapSize; - int hZ = (startingZ - zoom) + addZ + _halfMapSize; + int hX = x + addX + HALF_WORLD_SIZE; + int hZ = (startingZ - zoom) + addZ + HALF_WORLD_SIZE; - if (hX >= _halfMapSize * 2 || hZ >= _halfMapSize * 2) + if (hX >= HALF_WORLD_SIZE * 2 || hZ >= HALF_WORLD_SIZE * 2) { continue; } @@ -331,7 +425,7 @@ public class ItemMapManager extends MiniPlugin } } - for (int z = startingZ; z < startingZ + _blocksScan; z += zoom) + for (int z = startingZ; z < startingZ + BLOCK_SCAN_INTERVAL; z += zoom) { // Water depth colors not included double d1 = 0; @@ -340,10 +434,10 @@ public class ItemMapManager extends MiniPlugin { for (int addZ = 0; addZ < zoom; addZ++) { - int hX = x + addX + _halfMapSize; - int hZ = z + addZ + _halfMapSize; + int hX = x + addX + HALF_WORLD_SIZE; + int hZ = z + addZ + HALF_WORLD_SIZE; - if (hX >= _halfMapSize * 2 || hZ >= _halfMapSize * 2) + if (hX >= HALF_WORLD_SIZE * 2 || hZ >= HALF_WORLD_SIZE * 2) { continue; } @@ -370,7 +464,7 @@ public class ItemMapManager extends MiniPlugin b0 = 0; } - int origColor = map[(x + _halfMapSize) / zoom][(z + _halfMapSize) / zoom] - 1; + int origColor = map[(x + HALF_WORLD_SIZE) / zoom][(z + HALF_WORLD_SIZE) / zoom] - 1; /*if (color < 4) { @@ -388,7 +482,7 @@ public class ItemMapManager extends MiniPlugin }*/ byte color = (byte) (origColor + b0); - map[(x + _halfMapSize) / zoom][(z + _halfMapSize) / zoom] = color; + map[(x + HALF_WORLD_SIZE) / zoom][(z + HALF_WORLD_SIZE) / zoom] = color; } } } @@ -399,9 +493,9 @@ public class ItemMapManager extends MiniPlugin Byte[][] second = _map.get(scale); int zoom = getZoom(scale); - for (int x = startingX; x < startingX + _blocksScan; x += zoom) + for (int x = startingX; x < startingX + BLOCK_SCAN_INTERVAL; x += zoom) { - for (int z = startingZ; z < startingZ + _blocksScan; z += zoom) + for (int z = startingZ; z < startingZ + BLOCK_SCAN_INTERVAL; z += zoom) { HashMultiset hashmultiset = HashMultiset.create(); @@ -409,8 +503,8 @@ public class ItemMapManager extends MiniPlugin { for (int addZ = 0; addZ < zoom; addZ++) { - int pX = x + addX + _halfMapSize; - int pZ = z + addZ + _halfMapSize; + int pX = x + addX + HALF_WORLD_SIZE; + int pZ = z + addZ + HALF_WORLD_SIZE; if (pX >= first.length || pZ >= first.length) { @@ -432,7 +526,7 @@ public class ItemMapManager extends MiniPlugin { color = (byte) 0; } - second[(x + _halfMapSize) / zoom][(z + _halfMapSize) / zoom] = color; + second[(x + HALF_WORLD_SIZE) / zoom][(z + HALF_WORLD_SIZE) / zoom] = color; } } } @@ -440,25 +534,16 @@ public class ItemMapManager extends MiniPlugin @EventHandler public void dropItem(ItemSpawnEvent event) { - ItemStack item = event.getEntity().getItemStack(); - - if (item != null && item.getType() == Material.MAP && item.getDurability() >= _mapId - && item.getDurability() <= _mapId + 100) - { + if (isItemClansMap(event.getEntity().getItemStack())) event.getEntity().remove(); - } } - + public void removeMap(Player player) { for (int slot = 0; slot < player.getInventory().getSize(); slot++) { - ItemStack item = player.getInventory().getItem(slot); - - if (item != null && item.getType() == Material.MAP && item.getDurability() >= _mapId && item.getDurability() <= _mapId + 100) - { + if (isItemClansMap(player.getInventory().getItem(slot))) player.getInventory().setItem(slot, null); - } } } @@ -477,7 +562,7 @@ public class ItemMapManager extends MiniPlugin private double getDistance(Entry entry, double x1, double z1) { - return getDistance(x1, z1, entry.getKey() + (_blocksScan / 2), entry.getValue() + (_blocksScan / 2)); + return getDistance(x1, z1, entry.getKey() + (BLOCK_SCAN_INTERVAL / 2), entry.getValue() + (BLOCK_SCAN_INTERVAL / 2)); } public Byte[][] getMap(int scale) @@ -492,7 +577,7 @@ public class ItemMapManager extends MiniPlugin public int getMapSize() { - return _halfMapSize; + return HALF_WORLD_SIZE; } public int getZoom(int scale) @@ -500,35 +585,7 @@ public class ItemMapManager extends MiniPlugin return _scale.get(scale); } - @EventHandler - public void preventMapMoveInventories(InventoryClickEvent event) - { - Inventory inv = event.getClickedInventory(); - - if (inv == null) - return; - - // Yeah, the loop looks a little weird.. - for (ItemStack item : new ItemStack[] - { - event.getCurrentItem(), event.getCursor() - }) - { - if (item == null || item.getType() != Material.MAP || item.getDurability() < _mapId - || item.getDurability() > _mapId + 100) - continue; - - if (inv.getHolder() instanceof Player ? !event.isShiftClick() : Objects.equal(event.getCurrentItem(), item)) - continue; - - event.setCancelled(true); - - UtilPlayer.message(event.getWhoClicked(), - F.main("Inventory", "You cannot move " + F.item("Clans Map") + " between inventories.")); - return; - } - } - + //fixme So what appears to happen is that after you die, if your map is is the same then the map is frozen @EventHandler public void onDeath(PlayerDeathEvent event) { @@ -542,14 +599,10 @@ public class ItemMapManager extends MiniPlugin { Player player = event.getPlayer(); - ItemStack item = player.getInventory().getItem(event.getNewSlot()); - - if (item == null || item.getType() != Material.MAP || item.getDurability() < _mapId - || item.getDurability() > _mapId + 100) + if (!isItemClansMap(player.getInventory().getItem(event.getNewSlot()))) return; showZoom(player, getMap(player)); - } @EventHandler @@ -558,10 +611,7 @@ public class ItemMapManager extends MiniPlugin if (event.getAction() == Action.PHYSICAL) return; - ItemStack item = event.getItem(); - - if (item == null || item.getType() != Material.MAP || item.getDurability() < _mapId - || item.getDurability() > _mapId + 100) + if (!isItemClansMap(event.getItem())) return; event.setCancelled(true); @@ -666,36 +716,11 @@ public class ItemMapManager extends MiniPlugin private void rebuildScan() { - for (int x = -_halfMapSize; x < _halfMapSize; x += _blocksScan) + for (int x = -HALF_WORLD_SIZE; x < HALF_WORLD_SIZE; x += BLOCK_SCAN_INTERVAL) { - for (int z = -_halfMapSize - 16; z < _halfMapSize; z += (z < -_halfMapSize ? 16 : _blocksScan)) + for (int z = -HALF_WORLD_SIZE - 16; z < HALF_WORLD_SIZE; z += (z < -HALF_WORLD_SIZE ? 16 : BLOCK_SCAN_INTERVAL)) { - _scanList.add(new HashMap.SimpleEntry(x, z)); - } - } - - if (!_loadWorld) - { - Iterator> itel = _scanList.iterator(); - - while (itel.hasNext()) - { - Entry entry = itel.next(); - boolean removeEntry = true; - - for (Player player : UtilServer.getPlayers()) - { - if (Math.sqrt(getDistance(entry, player.getLocation().getX(), player.getLocation().getZ())) < 200) - { - removeEntry = false; - break; - } - } - - if (removeEntry) - { - itel.remove(); - } + _scanList.add(new HashMap.SimpleEntry<>(x, z)); } } @@ -744,101 +769,117 @@ public class ItemMapManager extends MiniPlugin if (event.getType() != UpdateType.FAST) return; - if (_scanList.isEmpty()) + if (_scanList.isEmpty() && UtilServer.getPlayers().length > 0) { - if (_loadWorld) - { - for (int x = -_halfMapSize; x < _halfMapSize; x += _blocksScan) - { - for (int z = -_halfMapSize; z < _halfMapSize; z += _blocksScan) - { - drawWorldScale(3, x, z); - colorWorldHeight(3, x, z); - } - } - - System.out.print("Finished first map scan and render"); - } - - _loadWorld = false; - - if (UtilServer.getPlayers().length == 0) - return; - rebuildScan(); } - else if (_scanList.size() % 20 == 0) + + if (_scanList.size() % 20 == 0) { Collections.sort(_scanList, _comparator); } - + if (_scanList.isEmpty()) + { return; + } Entry entry = _scanList.remove(0); int startingX = entry.getKey(); int startingZ = entry.getValue(); - boolean outsideMap = startingZ < -_halfMapSize; + boolean outsideMap = startingZ < -HALF_WORLD_SIZE; + + scanWorldMap(startingX, startingZ, !outsideMap, false); - scanWorldMap(startingX, startingZ, !outsideMap); - if (outsideMap) - { return; - } for (int scale = 1; scale < _scale.size(); scale++) { - if (scale == 3 && _loadWorld) - continue; - - if (!outsideMap) - { - drawWorldScale(scale, startingX, startingZ); - } - + drawWorldScale(scale, startingX, startingZ); colorWorldHeight(scale, startingX, startingZ); } - + colorWorldHeight(0, startingX, startingZ); } - public void scanWorldMap(int startingX, int startingZ, boolean setColors) + + // Let's not create hundreds of thousands of BlockPositions + // Single thread = should be thread safe + private BlockPosition.MutableBlockPosition _blockPosition = new BlockPosition.MutableBlockPosition(); + + // Maps the cached chunks which were loaded from disk to save IO operations + private LongObjectHashMap _chunkCache = new LongObjectHashMap<>(); + + /* + * Remove the cached chunks when the real chunks are loaded in + */ + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void LoadChunk(ChunkLoadEvent event) + { + _chunkCache.remove(LongHash.toLong(event.getChunk().getX(), event.getChunk().getZ())); + } + + /* + * Given a particular coordinate, this method will scan up to BLOCK_SCAN_INTERVAL and record the color of ever 16th block + * If a chunk has not been loaded, the following steps will be taken: + * * Attempt to load the chunk from disk. + * * If the chunk could not be loaded, generate it froms scratch + * Otherwise, the loaded chunk will be used + */ + public void scanWorldMap(int startingX, int startingZ, boolean setColors, boolean isFirstScan) { Byte[][] map = _map.get(0); - - for (int beginX = startingX; beginX < startingX + _blocksScan; beginX += 16) + for (int beginX = startingX; beginX < startingX + BLOCK_SCAN_INTERVAL; beginX += 16) { - for (int beginZ = startingZ - (startingZ > -_halfMapSize ? 16 : 0); beginZ < startingZ - + (setColors ? _blocksScan : 16); beginZ += 16) + for (int beginZ = startingZ - (startingZ > -HALF_WORLD_SIZE ? 16 : 0); beginZ < startingZ + + (setColors ? BLOCK_SCAN_INTERVAL : 16); beginZ += 16) { - Chunk chunk = _world.getChunkAt(beginX / 16, beginZ / 16); - boolean loaded = false; - - if (!chunk.isLoaded()) + int chunkX = beginX / 16; + int chunkZ = beginZ / 16; + net.minecraft.server.v1_8_R3.Chunk nmsChunk = _chunkProviderServer.getChunkIfLoaded(chunkX, chunkZ); + if (nmsChunk == null) { - if (_loadWorld) + long key = LongHash.toLong(chunkX, chunkZ); + nmsChunk = _chunkCache.get(key); + if (nmsChunk == null) { - loaded = chunk.load(); - } - else - { - continue; + if (!isFirstScan) + { + continue; + } + try + { + Object[] data = _chunkRegionLoader.loadChunk(_nmsWorld, chunkX, chunkZ); + if (data == null) + { + // Something is wrong with the chunk + System.out.println("Chunk is not generated or missing level/block data. Regenerating (" + chunkX + "," + chunkZ + ")"); + nmsChunk = ((CraftChunk) _world.getChunkAt(chunkX, chunkZ)).getHandle(); + } + else + { + nmsChunk = (net.minecraft.server.v1_8_R3.Chunk) data[0]; + } + } + catch (IOException e) + { + throw new RuntimeException("Chunk is corrupt or not readable!", e); + } + _chunkCache.put(key, nmsChunk); } } - net.minecraft.server.v1_8_R3.Chunk nmsChunk = ((CraftChunk) chunk).getHandle(); - - for (int x = beginX; x < beginX + 16; x++) + if (!nmsChunk.isEmpty()) { - for (int z = beginZ; z < beginZ + 16; z++) + for (int x = beginX; x < beginX + 16; x++) { - int color = 0; - - if (!nmsChunk.isEmpty()) + for (int z = beginZ; z < beginZ + 16; z++) { + int color = 0; + int k3 = x & 0xF; int l3 = z & 0xF; @@ -850,7 +891,8 @@ public class ItemMapManager extends MiniPlugin do { l4--; - iblockdata= nmsChunk.getBlockData(new BlockPosition(k3, l4, l3)); + _blockPosition.c(k3, l4, l3); + iblockdata = nmsChunk.getBlockData(_blockPosition); } while (iblockdata.getBlock().g(iblockdata) == MaterialMapColor.b && (l4 > 0)); @@ -860,34 +902,31 @@ public class ItemMapManager extends MiniPlugin Block block1; do { - block1 = nmsChunk.getType(new BlockPosition(k3, j5--, l3)); + _blockPosition.c(k3, j5--, l3); + block1 = nmsChunk.getType(_blockPosition); } while ((j5 > 0) && (block1.getMaterial().isLiquid())); } } - _heightMap[x + _halfMapSize + 16][z + _halfMapSize + 16] = l4; + _heightMap[x + HALF_WORLD_SIZE + 16][z + HALF_WORLD_SIZE + 16] = l4; if (setColors) { //color = block.f(i5).M; - IBlockData data = nmsChunk.getBlockData(new BlockPosition(k3, l4, l3)); + _blockPosition.c(k3, l4, l3); + IBlockData data = nmsChunk.getBlockData(_blockPosition); color = data.getBlock().g(data).M; color = (byte) ((color * 4) + 1); } - } - if (setColors && beginZ >= startingZ) - { - map[x + _halfMapSize][z + _halfMapSize] = (byte) color; + if (setColors && beginZ >= startingZ) + { + map[x + HALF_WORLD_SIZE][z + HALF_WORLD_SIZE] = (byte) color; + } } } - - if (loaded) - { - chunk.unload(); - } } } } @@ -898,10 +937,10 @@ public class ItemMapManager extends MiniPlugin PlayerGetMapEvent event = UtilServer.CallEvent(new PlayerGetMapEvent(player)); if (event.isCancelled()) return; - + for (ItemStack item : UtilInv.getItems(player)) { - if (item.getType() == Material.MAP && (item.getDurability() >= _mapId && item.getDurability() <= _mapId + 100)) + if (isItemClansMap(item)) { return; } @@ -909,62 +948,38 @@ public class ItemMapManager extends MiniPlugin ItemStack item = new ItemBuilder(Material.MAP, 1, (short) getMap(player).getMap()).setTitle("Clans Map").build(); - int slot = player.getInventory().firstEmpty(); + int slot = CLANS_MAP_SLOT; + + ItemStack mapSlot = player.getInventory().getItem(slot); + if (mapSlot != null && mapSlot.getType() != Material.AIR) + { + slot = player.getInventory().firstEmpty(); + } if (slot >= 0) { - ItemStack mapSlot = player.getInventory().getItem(8); - - if (mapSlot == null || mapSlot.getType() == Material.AIR) - { - slot = 8; - } - player.getInventory().setItem(slot, item); } } + /* + * Displays the action bar to a player given their zoom level. Implementation may change + */ private void showZoom(Player player, MapInfo info) { - String progressBar = C.cBlue + ""; - - boolean colorChange = false; - - for (int i = 2; i >= 0; i--) - { - if (!colorChange && i < info.getScale()) - { - progressBar += C.cGray; - colorChange = true; - } - - char c; - - switch (i) - { - case 0: - c = 'â–ˆ'; - break; - case 1: - c = 'â–†'; - break; - default: - c = 'â–„'; - break; - } - - for (int a = 0; a < 4; a++) - { - progressBar += c; - } - - if (i > 0) - { - progressBar += " "; - } - } - - UtilTextBottom.display(progressBar, player); + UtilTextBottom.display(ZOOM_INFO[info.getScale()], player); } + /* + * Check whether an {@link ItemStack} is also a Clans Map + * + * @param itemStack The {@link ItemStack} to check + * @returns Whether the {@link ItemStack} is also a Clans Map + */ + private boolean isItemClansMap(ItemStack itemStack) + { + return UtilItem.matchesMaterial(itemStack, Material.MAP) + && itemStack.getDurability() >= _mapId + && itemStack.getDurability() <= _mapId + 100; + } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/map/ItemMapRenderer.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/map/ItemMapRenderer.java index 746f48e7e..955249127 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/map/ItemMapRenderer.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/map/ItemMapRenderer.java @@ -50,7 +50,7 @@ public class ItemMapRenderer extends MapRenderer // } // else // { - renderNormalMap(mapView, canvas, player); + renderNormalMap(mapView, canvas, player); // } } @@ -135,7 +135,7 @@ public class ItemMapRenderer extends MapRenderer if (_manager.getClansUtility().relPT(player, chunk) == ClansUtility.ClanRelation.SAFE) clanColor2 = new Color(50, 150, 255); } - else if (owningClan.getName().equals("Spawn")) + else if (owningClan.getName().equals("Spawn")) { clanColor = Color.WHITE; clanColor2 = new Color(0, 255, 100); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/map/events/PlayerGetMapEvent.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/map/events/PlayerGetMapEvent.java index 5e5bccc4a..e13645b8a 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/map/events/PlayerGetMapEvent.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/map/events/PlayerGetMapEvent.java @@ -4,6 +4,9 @@ import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +/* + * This event is called when a Player is about to receive a Clans Map + */ public class PlayerGetMapEvent extends Event { private static final HandlerList handlers = new HandlerList(); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/murder/MurderManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/murder/MurderManager.java index 2f5fcaf30..8f10f9f0b 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/murder/MurderManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/murder/MurderManager.java @@ -73,7 +73,7 @@ public class MurderManager extends MiniClientPlugin } } - @EventHandler + @EventHandler (ignoreCancelled = true) public void onPickup(PlayerPickupItemEvent event) { refreshClient(event.getPlayer()); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/observer/ObserverManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/observer/ObserverManager.java index b5e97bdbc..606c1ec68 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/observer/ObserverManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/observer/ObserverManager.java @@ -131,7 +131,7 @@ public class ObserverManager extends MiniPlugin } } - @EventHandler + @EventHandler (priority = EventPriority.LOWEST) public void onPickup(PlayerPickupItemEvent event) { ObserverData data = _observerMap.get(event.getPlayer()); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/regions/ClansRegions.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/regions/ClansRegions.java index 797f63f24..6c9424634 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/regions/ClansRegions.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/regions/ClansRegions.java @@ -197,18 +197,7 @@ public class ClansRegions extends MiniPlugin { int x = chunkX + xOffset; int z = chunkZ + zOffset; - Chunk chunk; - try - { //Corrupted chunk will hold up whole server - chunk = location.getWorld().getChunkAt(x, z); - } - catch(Exception e) - { - System.out.println("UNABLE TO LOAD CHUNK AT " + x + " , " + z); - e.printStackTrace(); - continue; - } - String chunkStr = UtilWorld.chunkToStr(chunk); + String chunkStr = location.getWorld().getName() + "," + x + "," + z; if (addNegative) { diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/repository/SiegeWeaponRepository.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/repository/SiegeWeaponRepository.java index 302329ccb..c8a03daec 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/repository/SiegeWeaponRepository.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/repository/SiegeWeaponRepository.java @@ -132,7 +132,7 @@ public class SiegeWeaponRepository extends MinecraftRepository public void updateWeapon(SiegeWeaponToken token) { - System.out.println("Siege Repo> Updating weapon " + token.UniqueId); +// System.out.println("Siege Repo> Updating weapon " + token.UniqueId); _siegeManager.runAsync(() -> executeUpdate(UPDATE_WEAPON, diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/SiegeWeapon.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/SiegeWeapon.java index 451477876..33c7a5cba 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/SiegeWeapon.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/siege/weapon/SiegeWeapon.java @@ -27,6 +27,7 @@ import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.util.EulerAngle; import org.spigotmc.event.entity.EntityDismountEvent; @@ -559,6 +560,7 @@ public abstract class SiegeWeapon implements Listener { entity.setCustomName(Integer.toString(_uniqueId)); entity.setCustomNameVisible(false); + entity.setMetadata("Creature.DoNotDrop", new FixedMetadataValue(_clans.getPlugin(), true)); _comprisedOf.add(entity); @@ -569,6 +571,8 @@ public abstract class SiegeWeapon implements Listener { Entity entity = _entityMapping.get(uniqueName); + entity.removeMetadata("Creature.DoNotDrop", _clans.getPlugin()); + _entityMapping.remove(uniqueName); _comprisedOf.remove(entity); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/economy/GoldManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/economy/GoldManager.java index ab3a0b03c..a86f41fd2 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/economy/GoldManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/economy/GoldManager.java @@ -113,7 +113,7 @@ public class GoldManager extends MiniPlugin } } - @EventHandler + @EventHandler (ignoreCancelled = true) public void onPickup(PlayerPickupItemEvent event) { if (_itemSet.contains(event.getItem())) diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/gameplay/Gameplay.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/gameplay/Gameplay.java index 8f2a72d4a..207b16d16 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/gameplay/Gameplay.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/gameplay/Gameplay.java @@ -867,6 +867,12 @@ public class Gameplay extends MiniPlugin return; } } + + if (_blockRestore.contains(event.getClickedBlock())) + { + UtilPlayer.message(player, F.main("Repair", "You cannot repair using that anvil")); + return; + } // Repair! UtilPlayer.message(player, F.main("Repair", "You repaired " + F.item(item.getItemMeta().getDisplayName()) + ".")); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/CustomItem.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/CustomItem.java index d52ebc928..281846d4e 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/CustomItem.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/CustomItem.java @@ -29,7 +29,7 @@ import mineplex.minecraft.game.core.damage.CustomDamageEvent; * @author MrTwiggy * */ -public class CustomItem +public class CustomItem implements Listener { private static final ChatColor TITLE_COLOR = ChatColor.GOLD; // Chat color @@ -73,6 +73,7 @@ public class CustomItem _material = material; _attributes = new AttributeContainer(); _uuid = UUID.randomUUID().toString(); + UtilServer.RegisterEvents(this); } public CustomItem(Material material) diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/ItemAttribute.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/ItemAttribute.java index a2593c271..ab0cc3957 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/ItemAttribute.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/ItemAttribute.java @@ -1,8 +1,12 @@ package mineplex.game.clans.items.attributes; +import mineplex.game.clans.clans.ClansManager; +import mineplex.game.clans.clans.ClansUtility; import mineplex.game.clans.items.generation.ValueDistribution; import mineplex.minecraft.game.core.damage.CustomDamageEvent; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; @@ -127,4 +131,21 @@ public abstract class ItemAttribute } return s; } + + protected boolean isTeammate(Entity attacker, Entity defender) + { + if (attacker == null || defender == null) return false; + // Don't count attacks towards teammates + if (attacker instanceof Player && defender instanceof Player) + { + ClansUtility.ClanRelation relation = ClansManager.getInstance().getRelation((Player) attacker, (Player) defender); + if (relation == ClansUtility.ClanRelation.ALLY + || relation == ClansUtility.ClanRelation.SAFE + || relation == ClansUtility.ClanRelation.SELF) + { + return true; + } + } + return false; + } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/FlamingAttribute.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/FlamingAttribute.java index ec37a112a..d6a25bb23 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/FlamingAttribute.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/FlamingAttribute.java @@ -41,6 +41,7 @@ public class FlamingAttribute extends AttackAttribute if(attacker instanceof Player && ClansManager.getInstance().isSafe((Player) attacker)) return; if(defender instanceof Player && ClansManager.getInstance().isSafe((Player) defender)) return; if(attacker instanceof Player && ((Player)attacker).getGameMode().equals(GameMode.CREATIVE)) return; + if (isTeammate(attacker, defender)) return; defender.setFireTicks(_fireDuration); } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/FrostedAttribute.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/FrostedAttribute.java index ce21cacfd..0e8b8a8ac 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/FrostedAttribute.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/FrostedAttribute.java @@ -54,6 +54,7 @@ public class FrostedAttribute extends ItemAttribute if (victim != null) { + if (isTeammate(event.GetDamagerPlayer(true), victim)) return; victim.addPotionEffect(generateSlowEffect()); // Slow attacking player } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/HasteAttribute.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/HasteAttribute.java index c476fc2b5..0abc5d253 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/HasteAttribute.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/HasteAttribute.java @@ -42,6 +42,7 @@ public class HasteAttribute extends AttackAttribute @Override public void triggerAttack(Entity attacker, Entity defender) { + if (isTeammate(attacker, defender)) return; if (attacker instanceof Player) { Player player = (Player) attacker; diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/JaggedAttribute.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/JaggedAttribute.java index 1b532943b..0bdce1eba 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/JaggedAttribute.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/JaggedAttribute.java @@ -28,6 +28,7 @@ public class JaggedAttribute extends AttackAttribute { @Override public void triggerAttack(Entity attacker, Entity defender) { + if (isTeammate(attacker, defender)) return; defender.setVelocity(new Vector(0, 0, 0)); if (defender instanceof LivingEntity) ((LivingEntity) defender).addPotionEffect(new PotionEffect(PotionEffectType.SLOW, 20, 1, false, false)); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/VampiricAttribute.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/VampiricAttribute.java index 3589216a8..ea4279dab 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/VampiricAttribute.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/attributes/weapon/VampiricAttribute.java @@ -37,6 +37,8 @@ public class VampiricAttribute extends ItemAttribute public void onAttack(CustomDamageEvent event) { Player damager = event.GetDamagerPlayer(false); + + if (isTeammate(damager, event.GetDamageePlayer())) return; double damage = event.GetDamage(); double healAmount = damage * (_healPercent / 100d); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/GiantsBroadsword.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/GiantsBroadsword.java index 746ac5312..d3581ebc7 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/GiantsBroadsword.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/GiantsBroadsword.java @@ -1,5 +1,6 @@ package mineplex.game.clans.items.legendaries; +import mineplex.core.recharge.Recharge; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.entity.Player; @@ -14,7 +15,7 @@ import mineplex.minecraft.game.core.damage.CustomDamageEvent; public class GiantsBroadsword extends LegendaryItem { public static final int SLOW_AMPLIFIER = 43; - public static final int REGEN_AMPLIFIER = 3; + public static final int REGEN_AMPLIFIER = 1; public GiantsBroadsword() { @@ -60,6 +61,7 @@ public class GiantsBroadsword extends LegendaryItem private void buffPlayer(Player player) { grantPotionEffect(player, PotionEffectType.SLOW, 40, SLOW_AMPLIFIER); - grantPotionEffect(player, PotionEffectType.REGENERATION, 2, REGEN_AMPLIFIER); //Regen + if (Recharge.Instance.use(player, "Giants Broadsword Regen", 250L, false, false, false)) + grantPotionEffect(player, PotionEffectType.REGENERATION, 5, REGEN_AMPLIFIER); //Regen } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/HyperAxe.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/HyperAxe.java index 085cc7fb4..5a695ba27 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/HyperAxe.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/HyperAxe.java @@ -31,7 +31,7 @@ public class HyperAxe extends LegendaryItem C.cWhite + "blade can rip through any opponent.", C.cWhite + " ", C.cWhite + "Hit delay is reduced by " + C.cYellow + "50%", - C.cWhite + "Deals " + C.cYellow + "10 Damage" + C.cWhite + " with attack", + C.cWhite + "Deals " + C.cYellow + "3 Damage" + C.cWhite + " with attack", C.cYellow + "Right-Click" + C.cWhite + " to use " + C.cGreen + "Dash", }, Material.RECORD_3); _speedAmount = amountGen.generateIntValue(); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/MagneticMaul.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/MagneticMaul.java index 1494de126..3fb814e50 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/MagneticMaul.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/MagneticMaul.java @@ -1,11 +1,6 @@ package mineplex.game.clans.items.legendaries; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; -import org.bukkit.util.Vector; - +import mineplex.core.common.util.C; import mineplex.core.common.util.UtilAction; import mineplex.core.common.util.UtilAlg; import mineplex.core.common.util.UtilMath; @@ -18,6 +13,12 @@ import mineplex.core.common.util.UtilWorld; import mineplex.game.clans.clans.ClansManager; import mineplex.minecraft.game.core.damage.CustomDamageEvent; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.util.Vector; + public class MagneticMaul extends LegendaryItem { public static final double PULL_RANGE = 10d; @@ -27,9 +28,11 @@ public class MagneticMaul extends LegendaryItem public MagneticMaul() { - super("Magnetic Maul", new String[]{ - - }, Material.RECORD_5); + super("Magnetic Maul", new String[] { + C.cWhite + "This brutal weapon allows you to pull your enemies towards you with magnetic force!" + + " ", + C.cYellow + "Right-Click" + C.cWhite + " to use Maul." + }, Material.RECORD_5); } @Override diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/MeridianScepter.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/MeridianScepter.java index c333f642e..8cc649c12 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/MeridianScepter.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/MeridianScepter.java @@ -1,17 +1,6 @@ package mineplex.game.clans.items.legendaries; -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; -import org.bukkit.event.entity.EntityDamageEvent.DamageCause; -import org.bukkit.potion.PotionEffect; -import org.bukkit.potion.PotionEffectType; -import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.util.Vector; +import java.util.HashMap; import mineplex.core.common.util.C; import mineplex.core.common.util.F; @@ -20,26 +9,37 @@ import mineplex.core.common.util.RGBData; import mineplex.core.common.util.UtilBlock; import mineplex.core.common.util.UtilCollections; import mineplex.core.common.util.UtilColor; -import mineplex.core.common.util.UtilEnt; import mineplex.core.common.util.UtilParticle; import mineplex.core.common.util.UtilParticle.ParticleType; import mineplex.core.common.util.UtilParticle.ViewDist; -import mineplex.core.incognito.IncognitoManager; import mineplex.core.common.util.UtilPlayer; import mineplex.core.common.util.UtilServer; import mineplex.core.common.util.UtilShapes; import mineplex.core.common.util.UtilText; +import mineplex.core.common.util.UtilTime; +import mineplex.core.common.util.UtilTime.TimeUnit; import mineplex.core.recharge.Recharge; import mineplex.game.clans.clans.ClansManager; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.util.Vector; + public class MeridianScepter extends LegendaryItem { - private long _lastFire = System.currentTimeMillis(); private long _interactWait; private RGBData[] colors = { UtilColor.RgbPurple, UtilColor.RgbPurple.Lighten(), UtilColor.RgbPurple.Darken() }; - private int _witherDamageTimes = 5; + private HashMap _animations = new HashMap(); public MeridianScepter() { @@ -100,21 +100,14 @@ public class MeridianScepter extends LegendaryItem // If they are less than 0.5 blocks away if (player.getEyeLocation().subtract(0, .3, 0).distance(projectile) <= 2) - { - player.addPotionEffect(new PotionEffect(PotionEffectType.WITHER, 20 * _witherDamageTimes, 0)); + { + AttackAnimation aa = new AttackAnimation(player, shooter); + int i = UtilServer.getServer().getScheduler().scheduleSyncRepeatingTask(UtilServer.getPlugin(), () -> { + aa.update(); + }, 0, 1); + _animations.put(aa, i); - int time = 0; - - for (int i = 0; i < _witherDamageTimes; i++) - { - UtilServer.getServer().getScheduler().scheduleSyncDelayedTask(UtilServer.getPlugin(), () -> { - ClansManager.getInstance().getDamageManager().NewDamageEvent(player, shooter, null, - DamageCause.CUSTOM, 1.75, false, true, true, - shooter.getName(), "Meridian Scepter"); - }, ++time * 20); - } - - UtilPlayer.message(player, F.main("Clans", F.elem(player.getName()) + " hit you with a " + F.elem("Meridian Scepter") + C.mBody + ".")); + UtilPlayer.message(player, F.main("Clans", F.elem(shooter.getName()) + " hit you with a " + F.elem("Meridian Scepter") + C.mBody + ".")); UtilPlayer.message(shooter, F.main("Clans", "You hit " + F.elem(player.getName()) + " with your " + F.elem("Meridian Scepter") + C.mBody + ".")); } } @@ -258,4 +251,68 @@ public class MeridianScepter extends LegendaryItem UtilParticle.PlayParticleToAll(ParticleType.RED_DUST, loc, UtilCollections.random(colors).ToVector(), 1f, 0, ViewDist.LONG); } } + + private class AttackAnimation + { + private Player _hit, _shooter; + private double _step; + private double _radius; + private long _start, _lastStepIncrease; + + public AttackAnimation(Player hit, Player shooter) + { + _step = 0; + _start = System.currentTimeMillis(); + _lastStepIncrease = System.currentTimeMillis(); + _hit = hit; + _shooter = shooter; + _radius = 2; + } + + public void update() + { + if (_hit == null) + { + end(); + return; + } + if (UtilTime.elapsed(_lastStepIncrease, UtilTime.convert(1, TimeUnit.SECONDS, TimeUnit.MILLISECONDS))) + { + _step++; + _lastStepIncrease = System.currentTimeMillis(); + } + drawHelix(); + + if (UtilTime.elapsed(_start, 4000)) + { + _hit.getWorld().strikeLightningEffect(_hit.getLocation()); + ClansManager.getInstance().getDamageManager().NewDamageEvent(_hit, _shooter, null, + DamageCause.CUSTOM, 6, false, true, true, + _shooter.getName(), "Meridian Scepter"); + ClansManager.getInstance().getCondition().Factory().Blind("Meridian Scepter", _hit, _shooter, 1, 0, true, true, true); + end(); + return; + } + } + + private void end() + { + int id = _animations.remove(this); + Bukkit.getScheduler().cancelTask(id); + } + + private void drawHelix() + { + double height = Math.min(_step * 2, 8D); + + for (double y = 0; y <= height; y += .5) + { + double x = _radius * Math.cos(y); + double z = _radius * Math.sin(y); + Location play = _hit.getLocation().add(x, y, z); + + UtilParticle.PlayParticleToAll(ParticleType.WITCH_MAGIC, play, null, 0, 3, ViewDist.MAX); + } + } + } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/WindBlade.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/WindBlade.java index 1ef288b15..a467146bd 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/WindBlade.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/items/legendaries/WindBlade.java @@ -1,9 +1,14 @@ package mineplex.game.clans.items.legendaries; +import mineplex.game.clans.items.GearManager; +import mineplex.game.clans.items.PlayerGear; +import net.minecraft.server.v1_8_R3.PlayerConnection; import org.bukkit.Material; import org.bukkit.Sound; +import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; import org.bukkit.entity.Player; -import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.EventHandler; +import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.util.Vector; import mineplex.core.common.util.C; @@ -16,85 +21,101 @@ import mineplex.core.common.util.UtilParticle.ParticleType; import mineplex.core.common.util.UtilParticle.ViewDist; import mineplex.core.common.util.UtilPlayer; import mineplex.core.common.util.UtilTextBottom; -import mineplex.core.common.util.UtilTextMiddle; import mineplex.core.common.util.UtilTime; import mineplex.core.recharge.Recharge; import mineplex.minecraft.game.core.damage.CustomDamageEvent; +import java.lang.reflect.Field; + public class WindBlade extends LegendaryItem { + private static final Field G_FIELD; + + static + { + try + { + G_FIELD = PlayerConnection.class.getDeclaredField("g"); + G_FIELD.setAccessible(true); + } + catch (ReflectiveOperationException exception) + { + throw new RuntimeException("Could not reflectively access field", exception); + } + } + public static final double FLIGHT_VELOCITY = 0.75d; - + private double _power; private double _burnoutThreshold; - + private int _messageTimer; - + public WindBlade() { - super("Wind Blade", new String[] { + super("Wind Blade", new String[]{ C.cWhite + "Long ago, a race of cloud dwellers", C.cWhite + "terrorized the skies. A remnant of", C.cWhite + "their tyranny, this airy blade is", C.cWhite + "the last surviving memorium from", C.cWhite + "their final battle against the Titans.", " ", - C.cYellow + "Right-Click" + C.cWhite + " to use" + C.cGreen + " Fly", + C.cYellow + "Right-Click" + C.cWhite + " to use" + C.cGreen + " Fly", }, Material.GREEN_RECORD); } - + @Override public void update(Player wielder) { long burnoutRemaining = -1L; - + if (Recharge.Instance.Get(wielder) != null && Recharge.Instance.Get(wielder).containsKey("clans_legendary_windblade_burnout")) { burnoutRemaining = Recharge.Instance.Get(wielder).get("clans_legendary_windblade_burnout").GetRemaining(); } - + // Check if player is attempting to fly and activate if (isHoldingRightClick()) { if (canPropel(wielder)) { wielder.setFallDistance(0f); - + if (burnoutRemaining > 0) { _messageTimer++; - + if (_messageTimer % 4 == 0) { UtilParticle.PlayParticle(ParticleType.SMOKE, wielder.getLocation(), 0.f, 0.f, 0.f, .1f, 1, ViewDist.NORMAL); wielder.playSound(wielder.getLocation(), Sound.FIZZ, .5f, 1.f); - + removePower(0.15); - + _burnoutThreshold = 0; } - + if (_messageTimer % 5 == 0) { UtilPlayer.message(wielder, F.main("Wind Blade", "Flight power damaged whilst scraping the ground! Repairs will be finish in " + F.time(UtilTime.MakeStr(burnoutRemaining)) + ".")); } - + if (_messageTimer % 10 == 0) { wielder.playSound(wielder.getLocation(), Sound.ANVIL_USE, .5f, 1.5f); } - + return; } - + removePower(UtilEnt.isGrounded(wielder) ? 1.17 : .88); - + propelPlayer(wielder); UtilParticle.PlayParticle(ParticleType.EXPLODE, wielder.getLocation().add(0, 1, 0), 0, 0, 0, .1f, 3, ViewDist.NORMAL); - + wielder.playSound(wielder.getLocation(), Sound.FIRE, .25f, 1.75f); } - + if (UtilEnt.isGrounded(wielder)) { _burnoutThreshold++; @@ -109,44 +130,67 @@ public class WindBlade extends LegendaryItem { _burnoutThreshold = UtilMath.clamp(_burnoutThreshold - .5, 0, _burnoutThreshold); } - + if (UtilEnt.isGrounded(wielder, wielder.getLocation()) || UtilEnt.isGrounded(wielder, wielder.getLocation().subtract(0, 1, 0))) { addPower(0.65); } - + if (_burnoutThreshold > 15 && burnoutRemaining <= 0) { Recharge.Instance.use(wielder, "clans_legendary_windblade_burnout", 2500, false, false); } - + UtilTextBottom.displayProgress(UtilMath.clamp(_power, .0, 80.) / 80., wielder); } - + @Override public void onAttack(CustomDamageEvent event, Player wielder) { event.AddMod("Wind Blade", 6); } - + + @EventHandler + public void onFall(CustomDamageEvent event) + { + if (event.GetDamageePlayer() != null && event.GetCause() == EntityDamageEvent.DamageCause.FALL) + { + PlayerGear playerGear = GearManager.getInstance().getPlayerGear(event.GetDamageePlayer()); + if (playerGear.getWeapon() instanceof WindBlade) + { + event.SetCancelled("Wind Blade No Fall Damage"); + } + } + } + private void propelPlayer(Player player) { Vector direction = player.getLocation().getDirection().normalize(); direction.multiply(FLIGHT_VELOCITY); - + player.setVelocity(direction); + + PlayerConnection connection = ((CraftPlayer) player).getHandle().playerConnection; + try + { + G_FIELD.set(connection, 0); + } + catch (IllegalAccessException e) + { + new RuntimeException("Could not update g field", e).printStackTrace(); + } } - + private boolean canPropel(Player player) { return _power > 0 && !UtilItem.isLiquid(player.getLocation().getBlock().getType()); } - + private void addPower(double power) { _power = UtilMath.clamp(_power + power, -20, 80); } - + private void removePower(double power) { _power = UtilMath.clamp(_power - power, -20, 80); diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/spawn/Spawn.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/spawn/Spawn.java index 59d1070a5..92f29b3bf 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/spawn/Spawn.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/spawn/Spawn.java @@ -213,6 +213,15 @@ public class Spawn extends MiniPlugin } } + @EventHandler + public void ignoreVelocity(PlayerVelocityEvent event) + { + if (_clansManager.getClanUtility().isSafe(event.getPlayer())) + { + event.setCancelled(true); + } + } + @EventHandler public void onSkill(SkillTriggerEvent event) { @@ -391,13 +400,13 @@ public class Spawn extends MiniPlugin if (isInSpawn(victim)) { event.SetCancelled("Safe Zone"); - attemptNotify(attacker, "You cannot attack players who are in spawn!"); + attemptNotify(attacker, "You cannot attack players who are in a safe zone!"); return; } else if (isInSpawn(attacker) && !isCombatTagged(attacker)) { event.SetCancelled("Safe Zone"); - attemptNotify(attacker, "You cannot attack untagged players while in spawn!"); + attemptNotify(attacker, "You cannot attack untagged players while in a safe zone!"); return; } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/TutorialWorldManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/TutorialWorldManager.java index 40ac68fc6..e6714847a 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/TutorialWorldManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/TutorialWorldManager.java @@ -2,6 +2,7 @@ package mineplex.game.clans.tutorial; import java.io.File; import java.io.IOException; +import java.util.LinkedList; import java.util.Stack; import org.bukkit.Bukkit; @@ -25,7 +26,7 @@ public class TutorialWorldManager extends MiniPlugin private final World _tutorialWorld; private final Schematic _schematic; - private Stack _regionStack; + private LinkedList _regionStack; private TutorialRegion _centerRegion; public TutorialWorldManager(JavaPlugin plugin, String worldName, String schematicName) throws IOException @@ -56,7 +57,7 @@ public class TutorialWorldManager extends MiniPlugin private void populateRegionStack() { - _regionStack = new Stack<>(); + _regionStack = new LinkedList<>(); // Populate the stack with 100 available tutorial regions for (int x = 0; x < 10; x++) diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/command/FinishCommand.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/command/FinishCommand.java index 954db2c0d..e0e32c1dc 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/command/FinishCommand.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/command/FinishCommand.java @@ -10,14 +10,16 @@ public class FinishCommand extends CommandBase { public FinishCommand(TutorialManager plugin) { - super(plugin, Rank.JNR_DEV, "finish", "end"); + super(plugin, Rank.ALL, "finish", "end"); } @Override public void Execute(Player caller, String[] args) { - Plugin.finishTutorial(caller); - - + boolean testServer = Plugin.getPlugin().getConfig().getString("serverstatus.group").equalsIgnoreCase("Testing"); + if (_commandCenter.GetClientManager().hasRank(caller, testServer ? Rank.ALL : Rank.JNR_DEV)) + { + Plugin.finishTutorial(caller); + } } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/command/TutorialCommand.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/command/TutorialCommand.java index c9722a1bf..f13e5bfde 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/command/TutorialCommand.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/command/TutorialCommand.java @@ -1,19 +1,17 @@ package mineplex.game.clans.tutorial.command; -import org.bukkit.entity.Player; - -import mineplex.core.command.CommandBase; import mineplex.core.command.MultiCommandBase; import mineplex.core.common.Rank; import mineplex.core.common.util.F; import mineplex.core.common.util.UtilPlayer; import mineplex.game.clans.tutorial.TutorialManager; +import org.bukkit.entity.Player; public class TutorialCommand extends MultiCommandBase { public TutorialCommand(TutorialManager plugin) { - super(plugin, Rank.JNR_DEV, "tutorial", "tut"); + super(plugin, Rank.ALL, "tutorial", "tut"); AddCommand(new StartCommand(plugin)); AddCommand(new FinishCommand(plugin)); @@ -24,4 +22,14 @@ public class TutorialCommand extends MultiCommandBase { UtilPlayer.message(caller, F.main("Tutorial", "/tutorial start ")); } + + @Override + public void Execute(Player caller, String[] args) + { + boolean testServer = Plugin.getPlugin().getConfig().getString("serverstatus.group").equalsIgnoreCase("Testing"); + if (_commandCenter.GetClientManager().hasRank(caller, testServer ? Rank.ALL : Rank.JNR_DEV)) + { + super.Execute(caller, args); + } + } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/tutorials/clans/objective/goals/attackenemy/LoadCannonGoal.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/tutorials/clans/objective/goals/attackenemy/LoadCannonGoal.java index 81ca051c6..21b69d371 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/tutorials/clans/objective/goals/attackenemy/LoadCannonGoal.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/tutorial/tutorials/clans/objective/goals/attackenemy/LoadCannonGoal.java @@ -1,5 +1,7 @@ package mineplex.game.clans.tutorial.tutorials.clans.objective.goals.attackenemy; +import mineplex.core.common.util.F; +import mineplex.core.common.util.UtilPlayer; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -8,6 +10,8 @@ import mineplex.core.common.util.UtilInv; import mineplex.game.clans.clans.siege.events.LoadSiegeWeaponEvent; import mineplex.game.clans.tutorial.objective.ObjectiveGoal; import mineplex.game.clans.tutorial.tutorials.clans.objective.AttackEnemyObjective; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockPlaceEvent; public class LoadCannonGoal extends ObjectiveGoal { @@ -43,4 +47,15 @@ public class LoadCannonGoal extends ObjectiveGoal finish(event.getPlayer()); } + + @EventHandler (priority = EventPriority.MONITOR) + public void onBlockPlace(BlockPlaceEvent event) + { + if (!contains(event.getPlayer())) + { + return; + } + UtilPlayer.message(event.getPlayer(), F.main("Clans", "Are you sure? That's the only TNT you have!")); + event.setCancelled(true); + } } diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/world/WorldManager.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/world/WorldManager.java index f2761bab3..e6b7bf983 100644 --- a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/world/WorldManager.java +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/world/WorldManager.java @@ -1,150 +1,151 @@ package mineplex.game.clans.world; -import java.util.ArrayList; -import java.util.HashMap; +import java.util.*; -import org.bukkit.Bukkit; +import mineplex.core.common.util.UtilWorld; import org.bukkit.World; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; -import org.bukkit.entity.Item; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.plugin.java.JavaPlugin; import mineplex.core.MiniPlugin; -import mineplex.core.common.util.UtilMath; import mineplex.core.updater.UpdateType; import mineplex.core.updater.event.UpdateEvent; -import mineplex.game.clans.clans.ClansManager; public class WorldManager extends MiniPlugin -{ - public WorldManager(JavaPlugin plugin) +{ + private static final Map CULL_LIMITS = new HashMap<>(); + private static final Set MINECART_TYPES = new HashSet<>(); + private static final int MIN_RANGE = 64; + private static final int MIN_RANGE_SQUARED = MIN_RANGE * MIN_RANGE; + + static + { + // Animals + CULL_LIMITS.put(EntityType.BAT, 50); + CULL_LIMITS.put(EntityType.CHICKEN, 150); + CULL_LIMITS.put(EntityType.COW, 150); + CULL_LIMITS.put(EntityType.HORSE, 50); + CULL_LIMITS.put(EntityType.IRON_GOLEM, 50); + CULL_LIMITS.put(EntityType.MUSHROOM_COW, 50); + CULL_LIMITS.put(EntityType.OCELOT, 50); + CULL_LIMITS.put(EntityType.PIG, 150); + CULL_LIMITS.put(EntityType.RABBIT, 50); + CULL_LIMITS.put(EntityType.SHEEP, 150); + CULL_LIMITS.put(EntityType.WOLF, 150); + + // Monsters + CULL_LIMITS.put(EntityType.CAVE_SPIDER, 100); + CULL_LIMITS.put(EntityType.CREEPER, 100); + CULL_LIMITS.put(EntityType.ENDERMAN, 50); + CULL_LIMITS.put(EntityType.ENDERMITE, 50); + CULL_LIMITS.put(EntityType.SILVERFISH, 50); + CULL_LIMITS.put(EntityType.SKELETON, 100); + CULL_LIMITS.put(EntityType.SLIME, 50); + CULL_LIMITS.put(EntityType.SPIDER, 100); + CULL_LIMITS.put(EntityType.ZOMBIE, 100); + + // Nether + CULL_LIMITS.put(EntityType.BLAZE, 50); + CULL_LIMITS.put(EntityType.GHAST, 50); + CULL_LIMITS.put(EntityType.MAGMA_CUBE, 50); + CULL_LIMITS.put(EntityType.PIG_ZOMBIE, 50); + + MINECART_TYPES.add(EntityType.MINECART); + MINECART_TYPES.add(EntityType.MINECART_CHEST); + MINECART_TYPES.add(EntityType.MINECART_COMMAND); + MINECART_TYPES.add(EntityType.MINECART_FURNACE); + MINECART_TYPES.add(EntityType.MINECART_HOPPER); + MINECART_TYPES.add(EntityType.MINECART_MOB_SPAWNER); + MINECART_TYPES.add(EntityType.MINECART_TNT); + } + + public WorldManager(JavaPlugin plugin) { super("Clan World Manager", plugin); } - + @EventHandler public void cullMobs(UpdateEvent event) { if (event.getType() != UpdateType.SLOW) return; - + for (World world : getPlugin().getServer().getWorlds()) { - HashMap> _ents = new HashMap>(); - - for (EntityType type : EntityType.values()) - _ents.put(type, new ArrayList()); - - for (Entity ent : world.getEntities()) + List players = world.getPlayers(); + Map> entities = new HashMap<>(); + + for (Entity entity : world.getEntities()) { - if (ent.getCustomName() == null) + if (entity.getCustomName() != null) + { + continue; + } + + EntityType entityType = entity.getType(); + + if (entityType == EntityType.ARROW) + { + if (entity.getTicksLived() > 800) + { + entity.remove(); + } + } + else if (entityType == EntityType.DROPPED_ITEM) + { + if (entity.getTicksLived() > 2400) + { + entity.remove(); + } + } + else if (CULL_LIMITS.containsKey(entityType)) { boolean cull = true; - - for (Player player : world.getPlayers()) + for (Player player : players) { - if (player.getLocation().distance(ent.getLocation()) <= 64) + // Using NMS because this is going to be called quite a few times + // and each getLocation() call creates a new Location object + if (UtilWorld.distanceSquared(player, entity) <= MIN_RANGE_SQUARED) { cull = false; break; } } - if (cull) { - _ents.get(ent.getType()).add(ent); + entities.computeIfAbsent(entityType, key -> new HashSet<>()).add(entity); + } + } + else if (MINECART_TYPES.contains(entityType)) + { + if (entity.getTicksLived() > 800) + { + entity.remove(); } } } - - for (EntityType type : _ents.keySet()) + + for (Map.Entry> entry : entities.entrySet()) { - ArrayList ents = _ents.get(type); - - //Clean Old Arrows - if (type == EntityType.ARROW) - { - for (Entity ent : ents) - if (ent.getTicksLived() > 800) - ent.remove(); - } - - //Clean Old Items - if (type == EntityType.DROPPED_ITEM) - { - for (Entity ent : ents) - if (ent.getTicksLived() > 2400) - ent.remove(); - } - - //Animals - else if (type == EntityType.BAT) cull(ents, 50); - else if (type == EntityType.CHICKEN) cull(ents, 150); - else if (type == EntityType.COW) cull(ents, 150); - else if (type == EntityType.HORSE) cull(ents, 50); - else if (type == EntityType.IRON_GOLEM) cull(ents, 50); - else if (type == EntityType.MUSHROOM_COW) cull(ents, 50); - else if (type == EntityType.OCELOT) cull(ents, 50); - else if (type == EntityType.PIG) cull(ents, 150); - else if (type == EntityType.RABBIT) cull(ents, 50); - else if (type == EntityType.SHEEP) cull(ents, 150); - else if (type == EntityType.WOLF) cull(ents, 150); - - //Monster - else if (type == EntityType.CAVE_SPIDER) cull(ents, 100); - else if (type == EntityType.CREEPER) cull(ents, 100); - else if (type == EntityType.ENDERMAN) cull(ents, 50); - else if (type == EntityType.ENDERMITE) cull(ents, 50); - else if (type == EntityType.SILVERFISH) cull(ents, 50); - else if (type == EntityType.SKELETON) cull(ents, 100); - else if (type == EntityType.SLIME) cull(ents, 50); - else if (type == EntityType.SPIDER) cull(ents, 100); - else if (type == EntityType.ZOMBIE) cull(ents, 100); - - //Nether - else if (type == EntityType.BLAZE) cull(ents, 50); - else if (type == EntityType.GHAST) cull(ents, 50); - else if (type == EntityType.MAGMA_CUBE) cull(ents, 50); - else if (type == EntityType.PIG_ZOMBIE) cull(ents, 50); - - //Clean Old Minecarts - if (type == EntityType.MINECART || - type == EntityType.MINECART_CHEST || - type == EntityType.MINECART_COMMAND || - type == EntityType.MINECART_FURNACE || - type == EntityType.MINECART_HOPPER || - type == EntityType.MINECART_MOB_SPAWNER || - type == EntityType.MINECART_TNT) - { - for (Entity ent : ents) - if (ent.getTicksLived() > 800) - ent.remove(); - } + cull(entry.getKey(), entry.getValue(), CULL_LIMITS.get(entry.getKey())); } } } - - public void cull(ArrayList ents, int limit) - { - int culled = 0; - EntityType type = null; - while (ents.size() > limit) - { - Entity ent = ents.remove(UtilMath.r(ents.size())); - - type = ent.getType(); - ent.remove(); - + private void cull(EntityType type, Set ents, int limit) + { + Iterator iterator = ents.iterator(); + int culled = 0; + while (iterator.hasNext() && ents.size() > limit) + { + Entity entity = iterator.next(); + entity.remove(); + iterator.remove(); culled++; } - - if (type != null) - { - System.out.println("Culled " + culled + " " + type); - } + log("Culled " + culled + " " + type); } } diff --git a/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Assassin/Leap.java b/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Assassin/Leap.java index 06bdda17a..f1dcbb780 100644 --- a/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Assassin/Leap.java +++ b/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Assassin/Leap.java @@ -78,7 +78,7 @@ public class Leap extends SkillActive { //Action if (!wallkick) - UtilAction.velocity(player, 1.2, 0.2, 1, true); + UtilAction.velocity(player, 1 + 0.15 * level, 0.2, 1, true); else { Vector vec = player.getLocation().getDirection(); diff --git a/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Assassin/Recall.java b/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Assassin/Recall.java index b675e3a58..dc3fd9dde 100644 --- a/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Assassin/Recall.java +++ b/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Assassin/Recall.java @@ -59,12 +59,7 @@ public class Recall extends Skill public void use(PlayerDropItemEvent event) { Player player = event.getPlayer(); - - if (!(player.getOpenInventory().getTopInventory() instanceof CraftInventoryCrafting)) - { - return; - } - + int level = getLevel(player); if (level == 0) return; @@ -87,49 +82,40 @@ public class Recall extends Skill LinkedList locs = _locMap.remove(player); if (locs == null) return; - + LinkedList health = _healthMap.remove(player); if (health == null) return; - - //Heal - double newHealth = Math.min(health.getLast(), player.getHealth() + 3 + level); - player.setHealth(newHealth); - - //Effect - player.getWorld().playSound(player.getLocation(), Sound.ZOMBIE_UNFECT, 2f, 2f); - - //Teleport - Location current = player.getLocation(); - Location target = locs.getLast(); - - player.teleport(target); - - //Inform - UtilPlayer.message(player, F.main(GetClassType().name(), "You used " + F.skill(GetName(level)) + ".")); - - //Effect - player.getWorld().playSound(player.getLocation(), Sound.ZOMBIE_UNFECT, 2f, 2f); - - while (UtilMath.offset(current, target) > 0.5) - { - UtilParticle.PlayParticle(ParticleType.WITCH_MAGIC, current, 0, 1f, 0, 0, 1, - ViewDist.LONGER, UtilServer.getPlayers()); - current = current.add(UtilAlg.getTrajectory(current, target).multiply(0.1)); - } - } - - @EventHandler - public void closeInv(InventoryCloseEvent event) - { - if (getLevel(event.getPlayer()) == 0) - { - return; - } - - event.getPlayer().getInventory().addItem(event.getPlayer().getItemOnCursor()); - event.getPlayer().setItemOnCursor(null); + + Factory.runSync(() -> { + //Heal + double newHealth = Math.min(health.getLast(), player.getHealth() + 3 + level); + player.setHealth(newHealth); + + //Effect + player.getWorld().playSound(player.getLocation(), Sound.ZOMBIE_UNFECT, 2f, 2f); + + //Teleport + Location current = player.getLocation(); + Location target = locs.getLast(); + + player.teleport(target); + + //Inform + UtilPlayer.message(player, F.main(GetClassType().name(), "You used " + F.skill(GetName(level)) + ".")); + + //Effect + player.getWorld().playSound(player.getLocation(), Sound.ZOMBIE_UNFECT, 2f, 2f); + + while (UtilMath.offset(current, target) > 0.5) + { + UtilParticle.PlayParticle(ParticleType.WITCH_MAGIC, current, 0, 1f, 0, 0, 1, + ViewDist.LONGER, UtilServer.getPlayers()); + current = current.add(UtilAlg.getTrajectory(current, target).multiply(0.1)); + } + }); } + @EventHandler public void storeLocation(UpdateEvent event) diff --git a/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Brute/Takedown.java b/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Brute/Takedown.java index dde2e6a1b..09faffdd8 100644 --- a/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Brute/Takedown.java +++ b/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Brute/Takedown.java @@ -2,6 +2,7 @@ package mineplex.minecraft.game.classcombat.Skill.Brute; import java.util.HashMap; +import mineplex.minecraft.game.core.damage.CustomDamageEvent; import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.Sound; @@ -34,21 +35,21 @@ public class Takedown extends SkillActive { private HashMap _live = new HashMap(); - public Takedown(SkillFactory skills, String name, ClassType classType, SkillType skillType, - int cost, int levels, - int energy, int energyMod, - long recharge, long rechargeMod, boolean rechargeInform, - Material[] itemArray, - Action[] actionArray) + public Takedown(SkillFactory skills, String name, ClassType classType, SkillType skillType, + int cost, int levels, + int energy, int energyMod, + long recharge, long rechargeMod, boolean rechargeInform, + Material[] itemArray, + Action[] actionArray) { super(skills, name, classType, skillType, cost, levels, - energy, energyMod, - recharge, rechargeMod, rechargeInform, + energy, energyMod, + recharge, rechargeMod, rechargeInform, itemArray, actionArray); - SetDesc(new String[] + SetDesc(new String[] { "Hurl yourself towards an opponent.", "If you collide with them, you deal", @@ -58,7 +59,7 @@ public class Takedown extends SkillActive } @Override - public boolean CustomCheck(Player player, int level) + public boolean CustomCheck(Player player, int level) { if (player.getLocation().getBlock().getTypeId() == 8 || player.getLocation().getBlock().getTypeId() == 9) { @@ -76,7 +77,7 @@ public class Takedown extends SkillActive } @Override - public void Skill(Player player, int level) + public void Skill(Player player, int level) { //Action Vector vec = player.getLocation().getDirection(); @@ -108,11 +109,11 @@ public class Takedown extends SkillActive int level = getLevel(player); if (level == 0) continue; - if (!UtilTime.elapsed(_live.get(player), 1000)) + if (!UtilTime.elapsed(_live.get(player), 1000)) continue; - _live.remove(player); - } + _live.remove(player); + } //Collide for (Player player : GetUsers()) @@ -167,25 +168,28 @@ public class Takedown extends SkillActive int damage = 3 + (level); //Damage Event - Factory.Damage().NewDamageEvent(damagee, damager, null, + CustomDamageEvent customDamageEvent = Factory.Damage().NewDamageEvent(damagee, damager, null, DamageCause.CUSTOM, damage, false, true, false, - damager.getName(), GetName()); + damager.getName(), GetName()); - //Damage Event - Factory.Damage().NewDamageEvent(damager, damagee, null, - DamageCause.CUSTOM, damage/2, false, true, false, - damager.getName(), GetName() + " Recoil"); + if (!customDamageEvent.IsCancelled()) + { + //Damage Event + Factory.Damage().NewDamageEvent(damager, damagee, null, + DamageCause.CUSTOM, damage / 2, false, true, false, + damager.getName(), GetName() + " Recoil"); - //Conditions - Factory.Condition().Factory().Slow(GetName(), damagee, damager, 2.5 + 0.5*level, 3, false, true, true, true); - Factory.Condition().Factory().Slow(GetName(), damager, damager, 2.5 + 0.5*level, 3, false, true, true, true); + //Conditions + Factory.Condition().Factory().Slow(GetName(), damagee, damager, 2.5 + 0.5 * level, 3, false, true, true, true); + Factory.Condition().Factory().Slow(GetName(), damager, damager, 2.5 + 0.5 * level, 3, false, true, true, true); - //Inform - UtilPlayer.message(damager, F.main(GetClassType().name(), "You hit " + F.name(UtilEnt.getName(damagee)) + " with " + F.skill(GetName(level)) + ".")); - UtilPlayer.message(damagee, F.main(GetClassType().name(), F.name(damager.getName()) + " hit you with " + F.skill(GetName(level)) + ".")); + //Inform + UtilPlayer.message(damager, F.main(GetClassType().name(), "You hit " + F.name(UtilEnt.getName(damagee)) + " with " + F.skill(GetName(level)) + ".")); + UtilPlayer.message(damagee, F.main(GetClassType().name(), F.name(damager.getName()) + " hit you with " + F.skill(GetName(level)) + ".")); - //Sound - damager.getWorld().playSound(damager.getLocation(), Sound.ZOMBIE_WOOD, 1f, 0.5f); + //Sound + damager.getWorld().playSound(damager.getLocation(), Sound.ZOMBIE_WOOD, 1f, 0.5f); + } } @EventHandler diff --git a/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Knight/HoldPosition.java b/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Knight/HoldPosition.java index 4aea8ddfd..9741ea52d 100644 --- a/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Knight/HoldPosition.java +++ b/Plugins/Mineplex.Minecraft.Game.ClassCombat/src/mineplex/minecraft/game/classcombat/Skill/Knight/HoldPosition.java @@ -1,5 +1,6 @@ package mineplex.minecraft.game.classcombat.Skill.Knight; +import mineplex.minecraft.game.core.condition.events.ConditionExpireEvent; import org.bukkit.Effect; import org.bukkit.Material; import org.bukkit.Sound; @@ -22,6 +23,10 @@ import mineplex.core.updater.UpdateType; import mineplex.core.updater.event.UpdateEvent; import mineplex.minecraft.game.classcombat.Skill.SkillActive; import mineplex.minecraft.game.classcombat.Skill.SkillFactory; +import org.bukkit.event.player.PlayerQuitEvent; + +import java.util.HashMap; +import java.util.Map; public class HoldPosition extends SkillActive { @@ -47,10 +52,13 @@ public class HoldPosition extends SkillActive }); } + private final Map _foodLevel = new HashMap<>(); + @Override public boolean CustomCheck(Player player, int level) { - if (player.getLocation().getBlock().getTypeId() == 8 || player.getLocation().getBlock().getTypeId() == 9) + Material type = player.getLocation().getBlock().getType(); + if (type == Material.WATER || type == Material.STATIONARY_WATER) { UtilPlayer.message(player, F.main("Skill", "You cannot use " + F.skill(GetName()) + " in water.")); return false; @@ -75,6 +83,8 @@ public class HoldPosition extends SkillActive //Effect player.getWorld().playSound(player.getLocation(), Sound.ENDERMAN_SCREAM, 1.5f, 0f); player.getWorld().playEffect(player.getLocation(), Effect.STEP_SOUND, 49); + + _foodLevel.put(player, player.getFoodLevel()); } @EventHandler(priority = EventPriority.HIGH) @@ -123,8 +133,32 @@ public class HoldPosition extends SkillActive } } + @EventHandler + public void on(ConditionExpireEvent event) + { + if (event.getCondition().GetReason().equals(GetName()) && event.getCondition().GetEnt() instanceof Player) + { + if (event.getCondition().GetType() == ConditionType.DAMAGE_RESISTANCE) + { + Player player = ((Player) event.getCondition().GetEnt()); + if (_foodLevel.get(player) != null) + { + player.setFoodLevel(_foodLevel.get(player)); + } + // Could be null value. Sanity check + _foodLevel.remove(player); + } + } + } + + @EventHandler + public void on(PlayerQuitEvent event) + { + _foodLevel.remove(event.getPlayer()); + } + @Override - public void Reset(Player player) + public void Reset(Player player) { player.setFoodLevel(20); } diff --git a/Plugins/Mineplex.Minecraft.Game.Core/src/mineplex/minecraft/game/core/condition/ConditionManager.java b/Plugins/Mineplex.Minecraft.Game.Core/src/mineplex/minecraft/game/core/condition/ConditionManager.java index 82f16e341..dbac07668 100644 --- a/Plugins/Mineplex.Minecraft.Game.Core/src/mineplex/minecraft/game/core/condition/ConditionManager.java +++ b/Plugins/Mineplex.Minecraft.Game.Core/src/mineplex/minecraft/game/core/condition/ConditionManager.java @@ -7,6 +7,7 @@ import java.util.Map.Entry; import java.util.WeakHashMap; import mineplex.core.MiniPlugin; +import mineplex.core.common.util.UtilServer; import mineplex.core.recharge.Recharge; import mineplex.core.updater.event.UpdateEvent; import mineplex.core.updater.UpdateType; @@ -18,6 +19,7 @@ import mineplex.core.common.util.UtilTime; import mineplex.core.common.util.UtilTime.TimeUnit; import mineplex.minecraft.game.core.condition.Condition.ConditionType; import mineplex.minecraft.game.core.condition.events.ConditionApplyEvent; +import mineplex.minecraft.game.core.condition.events.ConditionExpireEvent; import mineplex.minecraft.game.core.damage.DamageManager; import org.bukkit.Material; @@ -189,7 +191,11 @@ public class ConditionManager extends MiniPlugin Condition cond = conditionIterator.next(); if (cond.Tick()) + { + ConditionExpireEvent conditionExpireEvent = new ConditionExpireEvent(cond); + UtilServer.CallEvent(conditionExpireEvent); conditionIterator.remove(); + } } } diff --git a/Plugins/Mineplex.Minecraft.Game.Core/src/mineplex/minecraft/game/core/condition/events/ConditionExpireEvent.java b/Plugins/Mineplex.Minecraft.Game.Core/src/mineplex/minecraft/game/core/condition/events/ConditionExpireEvent.java new file mode 100644 index 000000000..9640d445a --- /dev/null +++ b/Plugins/Mineplex.Minecraft.Game.Core/src/mineplex/minecraft/game/core/condition/events/ConditionExpireEvent.java @@ -0,0 +1,33 @@ +package mineplex.minecraft.game.core.condition.events; + +import mineplex.minecraft.game.core.condition.Condition; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class ConditionExpireEvent extends Event +{ + private static final HandlerList handlers = new HandlerList(); + + private Condition _cond; + + public ConditionExpireEvent(Condition cond) + { + _cond = cond; + } + + public HandlerList getHandlers() + { + return handlers; + } + + public static HandlerList getHandlerList() + { + return handlers; + } + + public Condition getCondition() + { + return _cond; + } + +} diff --git a/Plugins/Mineplex.Minecraft.Game.Core/src/mineplex/minecraft/game/core/damage/DamageManager.java b/Plugins/Mineplex.Minecraft.Game.Core/src/mineplex/minecraft/game/core/damage/DamageManager.java index de559f25b..daba8449a 100644 --- a/Plugins/Mineplex.Minecraft.Game.Core/src/mineplex/minecraft/game/core/damage/DamageManager.java +++ b/Plugins/Mineplex.Minecraft.Game.Core/src/mineplex/minecraft/game/core/damage/DamageManager.java @@ -171,37 +171,39 @@ public class DamageManager extends MiniPlugin } */ - public void NewDamageEvent(LivingEntity damagee, LivingEntity damager, Projectile proj, + public CustomDamageEvent NewDamageEvent(LivingEntity damagee, LivingEntity damager, Projectile proj, DamageCause cause, double damage, boolean knockback, boolean ignoreRate, boolean ignoreArmor, String source, String reason) { - NewDamageEvent(damagee, damager, proj, + return NewDamageEvent(damagee, damager, proj, cause, damage, knockback, ignoreRate, ignoreArmor, source, reason, false); } - public void NewDamageEvent(LivingEntity damagee, LivingEntity damager, Projectile proj, + public CustomDamageEvent NewDamageEvent(LivingEntity damagee, LivingEntity damager, Projectile proj, DamageCause cause, double damage, boolean knockback, boolean ignoreRate, boolean ignoreArmor, String source, String reason, boolean cancelled) { - NewDamageEvent(damagee, damager, proj, null, cause, damage, knockback, ignoreRate, ignoreArmor, source, reason, cancelled); + return NewDamageEvent(damagee, damager, proj, null, cause, damage, knockback, ignoreRate, ignoreArmor, source, reason, cancelled); } - - public void NewDamageEvent(LivingEntity damagee, LivingEntity damager, Projectile proj, Location knockbackOrigin, + + public CustomDamageEvent NewDamageEvent(LivingEntity damagee, LivingEntity damager, Projectile proj, Location knockbackOrigin, DamageCause cause, double damage, boolean knockback, boolean ignoreRate, boolean ignoreArmor, String source, String reason) { - NewDamageEvent(damagee, damager, proj, knockbackOrigin, cause, damage, knockback, ignoreRate, ignoreArmor, source, + return NewDamageEvent(damagee, damager, proj, knockbackOrigin, cause, damage, knockback, ignoreRate, ignoreArmor, source, reason, false); } - public void NewDamageEvent(LivingEntity damagee, LivingEntity damager, Projectile proj, Location knockbackOrigin, + public CustomDamageEvent NewDamageEvent(LivingEntity damagee, LivingEntity damager, Projectile proj, Location knockbackOrigin, DamageCause cause, double damage, boolean knockback, boolean ignoreRate, boolean ignoreArmor, String source, String reason, boolean cancelled) - { - _plugin.getServer().getPluginManager().callEvent(new CustomDamageEvent(damagee, damager, proj, knockbackOrigin, cause, - damage, knockback, ignoreRate, ignoreArmor, source, reason, cancelled)); - } + { + CustomDamageEvent customDamageEvent = new CustomDamageEvent(damagee, damager, proj, knockbackOrigin, cause, + damage, knockback, ignoreRate, ignoreArmor, source, reason, cancelled); + _plugin.getServer().getPluginManager().callEvent(customDamageEvent); + return customDamageEvent; + } @EventHandler(priority = EventPriority.LOW) public void CancelDamageEvent(CustomDamageEvent event) @@ -282,21 +284,21 @@ public class DamageManager extends MiniPlugin if (e.equals(Enchantment.PROTECTION_ENVIRONMENTAL)) event.AddMod("Ench Prot", damagee.getName(), 0.5 * (double)enchants.get(e), false); - else if (e.equals(Enchantment.PROTECTION_FIRE) && + else if (e.equals(Enchantment.PROTECTION_FIRE) && event.GetCause() == DamageCause.FIRE && event.GetCause() == DamageCause.FIRE_TICK && event.GetCause() == DamageCause.LAVA) event.AddMod("Ench Prot", damagee.getName(), 0.5 * (double)enchants.get(e), false); - else if (e.equals(Enchantment.PROTECTION_FALL) && + else if (e.equals(Enchantment.PROTECTION_FALL) && event.GetCause() == DamageCause.FALL) event.AddMod("Ench Prot", damagee.getName(), 0.5 * (double)enchants.get(e), false); - else if (e.equals(Enchantment.PROTECTION_EXPLOSIONS) && + else if (e.equals(Enchantment.PROTECTION_EXPLOSIONS) && event.GetCause() == DamageCause.ENTITY_EXPLOSION) event.AddMod("Ench Prot", damagee.getName(), 0.5 * (double)enchants.get(e), false); - else if (e.equals(Enchantment.PROTECTION_PROJECTILE) && + else if (e.equals(Enchantment.PROTECTION_PROJECTILE) && event.GetCause() == DamageCause.PROJECTILE) event.AddMod("Ench Prot", damagee.getName(), 0.5 * (double)enchants.get(e), false); } @@ -322,7 +324,7 @@ public class DamageManager extends MiniPlugin else if (e.equals(Enchantment.FIRE_ASPECT)) if (_conditionManager != null) - _conditionManager.Factory().Ignite("Ench Fire", event.GetDamageeEntity(), damager, + _conditionManager.Factory().Ignite("Ench Fire", event.GetDamageeEntity(), damager, 4 * (double)enchants.get(e), false, false); } } @@ -351,8 +353,15 @@ public class DamageManager extends MiniPlugin } } } - - //Debug + } + + /* + * Should only be used to debug the damage event + * No modification of the event should take place + */ + @EventHandler (priority = EventPriority.MONITOR) + public void debugDamageEvent(CustomDamageEvent event) + { DisplayDamage(event); } @@ -529,7 +538,7 @@ public class DamageManager extends MiniPlugin } } - @EventHandler + @EventHandler (priority = EventPriority.MONITOR) public void DamageSound(CustomDamageEvent event) { if (event.IsCancelled()) diff --git a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/managers/MiscManager.java b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/managers/MiscManager.java index e68656e2c..b4d2993d1 100644 --- a/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/managers/MiscManager.java +++ b/Plugins/Nautilus.Game.Arcade/src/nautilus/game/arcade/managers/MiscManager.java @@ -140,7 +140,7 @@ public class MiscManager implements Listener @EventHandler public void HubCommand(PlayerCommandPreprocessEvent event) { - if (event.getMessage().toLowerCase().startsWith("/lobby") || event.getMessage().toLowerCase().startsWith("/hub") || event.getMessage().toLowerCase().startsWith("/leave")) + if (event.getMessage().toLowerCase().equals("/lobby") || event.getMessage().toLowerCase().equals("/hub") || event.getMessage().toLowerCase().equals("/leave")) { Manager.GetPortal().sendPlayerToServer(event.getPlayer(), "Lobby"); event.setCancelled(true);