Attempt to properly update...

This commit is contained in:
garbagemule
2012-03-01 00:19:17 +01:00
parent 2e76b93837
commit a3fa0539ce
29 changed files with 1972 additions and 0 deletions
@@ -0,0 +1,799 @@
package com.garbagemule.MobArena;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.ContainerBlock;
import org.bukkit.block.Sign;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.entity.Slime;
import org.bukkit.entity.Wolf;
import org.bukkit.event.Event.Result;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockBurnEvent;
import org.bukkit.event.block.BlockEvent;
import org.bukkit.event.block.BlockFormEvent;
import org.bukkit.event.block.BlockIgniteEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.SignChangeEvent;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.event.entity.EntityCombustEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent;
import org.bukkit.event.entity.EntityTargetEvent;
import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
import org.bukkit.event.entity.EntityTargetEvent.TargetReason;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerAnimationEvent;
import org.bukkit.event.player.PlayerBucketEmptyEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerPreLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.Attachable;
import org.bukkit.material.Bed;
import org.bukkit.material.Door;
import org.bukkit.material.Redstone;
import com.garbagemule.MobArena.MAUtils;
import com.garbagemule.MobArena.MobArena;
import com.garbagemule.MobArena.Msg;
import com.garbagemule.MobArena.framework.Arena;
import com.garbagemule.MobArena.leaderboards.Leaderboard;
import com.garbagemule.MobArena.listeners.MAGlobalListener.TeleportResponse;
import com.garbagemule.MobArena.region.ArenaRegion;
import com.garbagemule.MobArena.region.RegionPoint;
import com.garbagemule.MobArena.repairable.*;
import com.garbagemule.MobArena.util.TextUtils;
import com.garbagemule.MobArena.util.config.ConfigSection;
import com.garbagemule.MobArena.waves.MABoss;
public class ArenaListener
{
private MobArena plugin;
private Arena arena;
private ArenaRegion region;
private MonsterManager monsters;
private boolean softRestore,
softRestoreDrops,
protect;
private boolean monsterExp,
monsterInfight,
pvpEnabled,
foodRegen,
lockFoodLevel;
private boolean allowTeleport,
canShare,
allowMonsters;
private Set<Player> banned;
public ArenaListener(Arena arena, MobArena plugin) {
this.plugin = plugin;
this.arena = arena;
this.region = arena.getRegion();
this.monsters = arena.getMonsterManager();
/*
* TODO: Figure out if this is really a good idea + It saves needing all
* those methods in Arena.java + It is relatively simple + It would be
* fairly easy to implement an observer pattern - More private fields -
* Uglier code
*/
ConfigSection s = arena.getSettings();
this.softRestore = s.getBoolean("soft-restore", false);
this.softRestoreDrops = s.getBoolean("soft-restore-drops", false);
this.protect = s.getBoolean("protect", true);
this.monsterExp = s.getBoolean("monster-exp", false);
this.monsterInfight = s.getBoolean("monster-infight", false);
this.pvpEnabled = s.getBoolean("pvp-enabled", false);
this.foodRegen = s.getBoolean("food-regen", false);
this.lockFoodLevel = s.getBoolean("lock-food-level", true);
this.allowTeleport = s.getBoolean("allow-teleporting", false);
this.canShare = s.getBoolean("share-items-in-arena", true);
this.allowMonsters = arena.getWorld().getAllowMonsters();
this.banned = new HashSet<Player>();
}
public void onBlockBreak(BlockBreakEvent event) {
if (onBlockDestroy(event))
return;
event.setCancelled(true);
}
public void onBlockBurn(BlockBurnEvent event) {
if (onBlockDestroy(event))
return;
event.setCancelled(true);
}
private boolean onBlockDestroy(BlockEvent event) {
if (!arena.getRegion().contains(event.getBlock().getLocation()) || arena.inEditMode() || (!arena.isProtected() && arena.isRunning()))
return true;
Block b = event.getBlock();
if (arena.removeBlock(b) || b.getType() == Material.TNT)
return true;
if (softRestore && arena.isRunning()) {
BlockState state = b.getState();
Repairable r = null;
if (state instanceof ContainerBlock)
r = new RepairableContainer(state);
else if (state instanceof Sign)
r = new RepairableSign(state);
else if (state.getData() instanceof Attachable)
r = new RepairableAttachable(state);
else
r = new RepairableBlock(state);
arena.addRepairable(r);
if (!softRestoreDrops)
b.setTypeId(0);
return true;
}
return false;
}
public void onBlockPlace(BlockPlaceEvent event) {
Block b = event.getBlock();
// If the event didn't happen in the region, or if in edit mode, ignore
if (!arena.getRegion().contains(b.getLocation()) || arena.inEditMode()) {
return;
}
// If the arena isn't running, or if the player isn't in the arena, cancel.
if (!arena.isRunning() || !arena.inArena(event.getPlayer())) {
event.setCancelled(true);
return;
}
// Otherwise, block was placed during a session.
arena.addBlock(b);
switch (b.getType()){
// For doors, add the block just above (so we get both halves)
case WOODEN_DOOR:
case IRON_DOOR_BLOCK:
arena.addBlock(b.getRelative(0, 1, 0));
break;
}
}
public void onBlockForm(BlockFormEvent event) {
if (!arena.getRegion().contains(event.getBlock().getLocation()))
return;
// If a snowman forms some snow on its path, add the block
if (event.getNewState().getType() == Material.SNOW)
arena.addBlock(event.getBlock());
}
public void onBlockIgnite(BlockIgniteEvent event) {
if (!arena.getRegion().contains(event.getBlock().getLocation()))
return;
switch (event.getCause()){
case LIGHTNING:
case SPREAD:
event.setCancelled(true);
break;
case FLINT_AND_STEEL:
if (arena.isRunning())
arena.addBlock(event.getBlock().getRelative(BlockFace.UP));
else
event.setCancelled(true);
break;
default:
break;
}
}
public void onSignChange(SignChangeEvent event) {
arena.setLeaderboard(new Leaderboard(plugin, arena, event.getBlock().getLocation()));
arena.getRegion().set(RegionPoint.LEADERBOARD, event.getBlock().getLocation());
Messenger.tellPlayer(event.getPlayer(), "Leaderboard made. Now set up the stat signs!");
}
public void onCreatureSpawn(CreatureSpawnEvent event) {
if (!arena.getRegion().contains(event.getLocation())) {
if (!event.isCancelled()) {
event.setCancelled(!allowMonsters);
}
return;
}
if (event.getSpawnReason() != SpawnReason.CUSTOM) {
event.setCancelled(true);
return;
}
LivingEntity entity = (LivingEntity) event.getEntity();
if (arena.isRunning() && entity instanceof Slime)
monsters.addMonster(entity);
// If running == true, setCancelled(false), and vice versa.
event.setCancelled(!arena.isRunning());
}
public void onEntityExplode(EntityExplodeEvent event) {
if (!monsters.getMonsters().contains(event.getEntity()) && !arena.getRegion().contains(event.getLocation(), 10))
return;
monsters.removeMonster(event.getEntity());
// Cancel if the arena isn't running
if (!arena.isRunning()) {
event.setCancelled(true);
return;
}
// Uncancel, just in case.
event.setCancelled(false);
// If the arena isn't destructible, just clear the blocklist.
if (!softRestore && protect) {
List<Block> blocks = new LinkedList<Block>(arena.getBlocks());
event.blockList().retainAll(blocks);
return;
}
if (!softRestoreDrops)
event.setYield(0);
// Handle all the blocks in the block list.
for (Block b : event.blockList()) {
BlockState state = b.getState();
if (state.getData() instanceof Door && ((Door) state.getData()).isTopHalf()) {
state = b.getRelative(BlockFace.DOWN).getState();
}
else if (state.getData() instanceof Bed && ((Bed) state.getData()).isHeadOfBed()) {
state = b.getRelative(((Bed) state.getData()).getFacing().getOppositeFace()).getState();
}
// Create a Repairable from the block.
Repairable r = null;
if (state instanceof ContainerBlock)
r = new RepairableContainer(state);
else if (state instanceof Sign)
r = new RepairableSign(state);
else if (state.getData() instanceof Bed)
r = new RepairableBed(state);
else if (state.getData() instanceof Door)
r = new RepairableDoor(state);
else if (state.getData() instanceof Attachable || state.getData() instanceof Redstone)
r = new RepairableAttachable(state);
else
r = new RepairableBlock(state);
// Cakes and liquids should just get removed. If player-placed block, drop as item.
Material mat = state.getType();
if (mat == Material.CAKE_BLOCK || mat == Material.WATER || mat == Material.LAVA)
arena.removeBlock(b);
else if (arena.removeBlock(b))
arena.getWorld().dropItemNaturally(b.getLocation(), new ItemStack(state.getTypeId(), 1));
else if (softRestore)
arena.addRepairable(r);
else
arena.queueRepairable(r);
}
}
/******************************************************
*
* DEATH LISTENERS
*
******************************************************/
public void onEntityDeath(EntityDeathEvent event) {
if (event instanceof PlayerDeathEvent) {
onPlayerDeath((PlayerDeathEvent) event, (Player) event.getEntity());
}
else if (monsters.removeMonster(event.getEntity())) {
onMonsterDeath(event);
}
}
private void onPlayerDeath(PlayerDeathEvent event, Player player) {
if (arena.inArena(player) || arena.inLobby(player)) {
event.getDrops().clear();
event.setDroppedExp(0);
event.setKeepLevel(true);
arena.playerDeath(player);
}
}
public boolean onPlayerRespawn(PlayerRespawnEvent event) {
Player p = event.getPlayer();
if (!arena.isDead(p)) {
return false;
}
Location loc = arena.getRespawnLocation(p);
event.setRespawnLocation(loc);
arena.playerRespawn(p);
return true;
}
private void onMonsterDeath(EntityDeathEvent event) {
EntityDamageEvent e1 = event.getEntity().getLastDamageCause();
EntityDamageByEntityEvent e2 = (e1 instanceof EntityDamageByEntityEvent) ? (EntityDamageByEntityEvent) e1 : null;
Entity damager = (e2 != null) ? e2.getDamager() : null;
// Make sure to grab the owner of a projectile/pet
if (damager instanceof Projectile) {
damager = ((Projectile) damager).getShooter();
}
else if (damager instanceof Wolf && arena.hasPet(damager)) {
damager = (Player) ((Wolf) damager).getOwner();
}
// If the damager was a player, add to kills.
if (damager instanceof Player) {
ArenaPlayer ap = arena.getArenaPlayer((Player) damager);
if (ap != null) {
ArenaPlayerStatistics stats = ap.getStats();
if (stats != null) {
ap.getStats().inc("kills");
}
}
}
if (!monsterExp)
event.setDroppedExp(0);
event.getDrops().clear();
List<ItemStack> loot = monsters.getLoot(event.getEntity());
if (loot != null && !loot.isEmpty()) {
event.getDrops().add(getRandomItem(loot));
}
return;
}
private ItemStack getRandomItem(List<ItemStack> stacks) {
return stacks.get((new Random()).nextInt(stacks.size()));
}
/******************************************************
*
* DAMAGE LISTENERS
*
******************************************************/
public void onEntityDamage(EntityDamageEvent event) {
Entity damagee = event.getEntity();
if (!arena.isRunning() || !arena.getRegion().contains(damagee.getLocation())) {
return;
}
EntityDamageByEntityEvent edbe = (event instanceof EntityDamageByEntityEvent) ? (EntityDamageByEntityEvent) event : null;
Entity damager = null;
if (edbe != null) {
damager = edbe.getDamager();
if (damager instanceof Projectile) {
damager = ((Projectile) damager).getShooter();
}
else if (damager instanceof Wolf && arena.hasPet(damager)) {
damager = (Player) ((Wolf) damager).getOwner();
}
}
// Pet wolf
if (damagee instanceof Wolf && arena.hasPet(damagee)) {
onPetDamage(event, (Wolf) damagee, damager);
}
// Player
else if (damagee instanceof Player) {
onPlayerDamage(event, (Player) damagee, damager);
}
// Boss
else if (monsters.getBossMonsters().contains(damagee)) {
onBossDamage(event, (LivingEntity) damagee, damager);
}
// Regular monster
else if (monsters.getMonsters().contains(damagee)) {
onMonsterDamage(event, damagee, damager);
}
}
private void onPlayerDamage(EntityDamageEvent event, Player player, Entity damager) {
// Cancel all damage in the lobby and spec area
if (arena.inLobby(player) || arena.inSpec(player)) {
event.setCancelled(true);
return;
}
// If PvP is disabled and damager is a player, cancel damage
else if (arena.inArena(player)) {
if (!pvpEnabled && damager instanceof Player) {
event.setCancelled(true);
return;
}
event.setCancelled(false);
arena.getArenaPlayer(player).getStats().add("dmgTaken", event.getDamage());
}
}
private void onPetDamage(EntityDamageEvent event, Wolf pet, Entity damager) {
event.setCancelled(true);
}
private void onMonsterDamage(EntityDamageEvent event, Entity monster, Entity damager) {
if (damager instanceof Player) {
Player p = (Player) damager;
if (!arena.inArena(p)) {
event.setCancelled(true);
return;
}
ArenaPlayerStatistics aps = arena.getArenaPlayer(p).getStats();
aps.add("dmgDone", event.getDamage());
aps.inc("hits");
}
else if (damager instanceof Wolf && arena.hasPet(damager)) {
event.setDamage(1);
Player p = (Player) ((Wolf) damager).getOwner();
ArenaPlayerStatistics aps = arena.getArenaPlayer(p).getStats();
aps.add("dmgDone", event.getDamage());
// arena.getArenaPlayer(p).getStats().dmgDone += event.getDamage();
}
else if (damager instanceof LivingEntity) {
if (!monsterInfight)
event.setCancelled(true);
}
}
private void onBossDamage(EntityDamageEvent event, LivingEntity monster, Entity damager) {
// Health the boss back up.
monster.setHealth(monster.getMaxHealth());
// Damage the underlying MABoss.
MABoss boss = monsters.getBoss(monster);
boss.damage(event.getDamage());
// If it died, remove it from the arena.
if (boss.isDead()) {
monsters.removeBoss(monster);
monster.damage(10000);
}
// And "cancel out" the damage.
event.setDamage(1);
}
public void onEntityCombust(EntityCombustEvent event) {
if (monsters.getMonsters().contains(event.getEntity()))
event.setCancelled(true);
}
public void onEntityTarget(EntityTargetEvent event) {
if (!arena.isRunning() || event.isCancelled())
return;
if (arena.hasPet(event.getEntity())) {
if (event.getReason() != TargetReason.TARGET_ATTACKED_OWNER && event.getReason() != TargetReason.OWNER_ATTACKED_TARGET)
return;
if (!(event.getTarget() instanceof Player))
return;
// If the target is a player, cancel.
event.setCancelled(true);
}
else if (monsters.getMonsters().contains(event.getEntity())) {
if (event.getReason() == TargetReason.FORGOT_TARGET)
event.setTarget(MAUtils.getClosestPlayer(plugin, event.getEntity(), arena));
else if (event.getReason() == TargetReason.TARGET_DIED)
event.setTarget(MAUtils.getClosestPlayer(plugin, event.getEntity(), arena));
else if (event.getReason() == TargetReason.TARGET_ATTACKED_ENTITY)
if (arena.hasPet(event.getTarget()))
event.setCancelled(true);
else if (event.getReason() == TargetReason.CLOSEST_PLAYER)
if (!arena.inArena((Player) event.getTarget()))
event.setCancelled(true);
}
}
public void onEntityChangeBlock(EntityChangeBlockEvent event) {
if (arena.getRegion().contains(event.getBlock().getLocation()))
event.setCancelled(true);
}
public void onEntityRegainHealth(EntityRegainHealthEvent event) {
if (!arena.isRunning())
return;
if (!(event.getEntity() instanceof Player) || !arena.inArena((Player) event.getEntity()))
return;
if (!foodRegen && event.getRegainReason() == RegainReason.SATIATED) {
event.setCancelled(true);
}
}
public void onFoodLevelChange(FoodLevelChangeEvent event) {
if (!arena.isRunning())
return;
if (!(event.getEntity() instanceof Player) || !arena.inArena((Player) event.getEntity()))
return;
// If the food level is locked, cancel all changes.
if (lockFoodLevel)
event.setCancelled(true);
}
public void onPlayerAnimation(PlayerAnimationEvent event) {
if (!arena.isRunning() || !arena.inArena(event.getPlayer()))
return;
arena.getArenaPlayer(event.getPlayer()).getStats().inc("swings");
}
public void onPlayerDropItem(PlayerDropItemEvent event) {
Player p = event.getPlayer();
// If the player is active in the arena, only cancel if sharing is not
// allowed
if (arena.inArena(p)) {
if (!canShare) {
Messenger.tellPlayer(p, Msg.LOBBY_DROP_ITEM);
event.setCancelled(true);
}
}
// Else, if the player is in the lobby or a spectator, just cancel
else if (arena.inLobby(p) || arena.inSpec(p)) {
Messenger.tellPlayer(p, Msg.LOBBY_DROP_ITEM);
event.setCancelled(true);
}
/*
* If the player is not in the arena in any way (as arena player, lobby
* player or a spectator), but they -are- in the region, it must mean
* they are trying to drop items when not allowed
*/
else if (region.contains(p.getLocation())) {
Messenger.tellPlayer(p, Msg.LOBBY_DROP_ITEM);
event.setCancelled(true);
}
/*
* If the player is in the banned set, it means they got kicked or
* disconnected during a session, meaning they are more than likely
* trying to steal items, if a PlayerDropItemEvent is fired.
*/
else if (banned.contains(p)) {
Messenger.warning("Player " + p.getName() + " tried to steal class items!");
event.setCancelled(true);
}
}
public void onPlayerBucketEmpty(PlayerBucketEmptyEvent event) {
if (!arena.getReadyPlayersInLobby().contains(event.getPlayer()) && !arena.inArena(event.getPlayer()))
return;
if (!arena.isRunning()) {
event.getBlockClicked().getRelative(event.getBlockFace()).setTypeId(0);
event.setCancelled(true);
return;
}
Block liquid = event.getBlockClicked().getRelative(event.getBlockFace());
arena.addBlock(liquid);
}
public void onPlayerInteract(PlayerInteractEvent event) {
Player p = event.getPlayer();
if (arena.inArena(p) || !arena.inLobby(p))
return;
// Player is in the lobby, so disallow using items.
Action a = event.getAction();
if (a == Action.RIGHT_CLICK_AIR || a == Action.RIGHT_CLICK_BLOCK) {
event.setUseItemInHand(Result.DENY);
event.setCancelled(true);
}
// If there's no block involved, just return.
if (!event.hasBlock())
return;
// Iron block
if (event.getClickedBlock().getTypeId() == 42) {
handleReadyBlock(p);
}
// Sign
else if (event.getClickedBlock().getState() instanceof Sign) {
Sign sign = (Sign) event.getClickedBlock().getState();
handleSign(sign, p);
}
}
private void handleReadyBlock(Player p) {
if (arena.getArenaPlayer(p).getArenaClass() != null) {
Messenger.tellPlayer(p, Msg.LOBBY_PLAYER_READY);
arena.playerReady(p);
}
else {
Messenger.tellPlayer(p, Msg.LOBBY_PICK_CLASS);
}
}
private void handleSign(Sign sign, Player p) {
// Check if the first line is a class name.
String className = ChatColor.stripColor(sign.getLine(0)).toLowerCase();
if (!arena.getClasses().containsKey(className) && !className.equals("random"))
return;
// Check for permission.
if (!plugin.has(p, "mobarena.classes." + className) && !className.equals("random")) {
Messenger.tellPlayer(p, Msg.LOBBY_CLASS_PERMISSION);
return;
}
// Delay the inventory stuff to ensure that right-clicking works.
delayAssignClass(p, className);
}
private void delayAssignClass(final Player p, final String className) {
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin,new Runnable() {
public void run() {
arena.assignClass(p, className);
if (!className.equalsIgnoreCase("random"))
Messenger.tellPlayer(p, Msg.LOBBY_CLASS_PICKED, TextUtils.camelCase(className), arena.getClassLogo(className));
else
Messenger.tellPlayer(p, Msg.LOBBY_CLASS_RANDOM);
}
});
}
public void onPlayerQuit(PlayerQuitEvent event) {
Player p = event.getPlayer();
if (!arena.isEnabled() || (!arena.inArena(p) && !arena.inLobby(p)))
return;
arena.playerLeave(p);
banned.add(p);
scheduleUnban(p, 20);
}
public void onPlayerKick(PlayerKickEvent event) {
Player p = event.getPlayer();
if (!arena.isEnabled() || (!arena.inArena(p) && !arena.inLobby(p))) {
return;
}
arena.playerLeave(p);
banned.add(p);
scheduleUnban(p, 20);
}
private void scheduleUnban(final Player p, int ticks) {
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
public void run() {
banned.remove(p);
}
}, ticks);
}
public TeleportResponse onPlayerTeleport(PlayerTeleportEvent event) {
if (!arena.isEnabled() || !region.isSetup() || arena.inEditMode() || allowTeleport) {
return TeleportResponse.IDGAF;
}
Location to = event.getTo();
Location from = event.getFrom();
Player p = event.getPlayer();
if (region.contains(from)) {
// Players not in the arena are free to warp out.
if (!arena.inArena(p) && !arena.inLobby(p) && !arena.inSpec(p)) {
return TeleportResponse.ALLOW;
}
// Covers the case in which both locations are in the arena.
if (region.contains(to) || region.isWarp(to) || to.equals(arena.getPlayerEntry(p))) {
return TeleportResponse.ALLOW;
}
Messenger.tellPlayer(p, Msg.WARP_FROM_ARENA);
return TeleportResponse.REJECT;
}
else if (region.contains(to)) {
if (region.isWarp(from) || region.isWarp(to) || to.equals(arena.getPlayerEntry(p))) {
return TeleportResponse.ALLOW;
}
Messenger.tellPlayer(p, Msg.WARP_TO_ARENA);
return TeleportResponse.REJECT;
}
return TeleportResponse.IDGAF;
}
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
Player p = event.getPlayer();
if (event.isCancelled() || (!arena.inArena(p) && !arena.inLobby(p))) {
return;
}
// This is safe, because commands will always have at least one element.
String base = event.getMessage().split(" ")[0];
// Check if the entire base command is allowed.
if (plugin.getArenaMaster().isAllowed(base)) {
return;
}
// If not, check if the specific command is allowed.
String noslash = event.getMessage().substring(1);
if (plugin.getArenaMaster().isAllowed(noslash)) {
return;
}
// This is dirty, but it ensures that commands are indeed blocked.
event.setMessage("/");
// Cancel the event regardless.
event.setCancelled(true);
Messenger.tellPlayer(p, Msg.MISC_COMMAND_NOT_ALLOWED);
}
public void onPlayerPreLogin(PlayerPreLoginEvent event) {
Player p = plugin.getServer().getPlayer(event.getName());
if (p == null || !p.isOnline()) return;
Arena arena = plugin.getArenaMaster().getArenaWithPlayer(p);
if (arena == null) return;
arena.playerLeave(p);
}
}
@@ -0,0 +1,18 @@
package com.garbagemule.MobArena.waves.ability;
import com.garbagemule.MobArena.framework.Arena;
import com.garbagemule.MobArena.waves.MABoss;
public interface Ability
{
/**
* Execute the boss ability.
* The ability should be concise and not start any unnecessary threading,
* or schedule any unnecessary tasks.
* The MABoss object can be used to get the LivingEntity that is the boss,
* and to damage, heal or just set the boss health directly.
* @param arena the Arena of the boss
* @param boss the MABoss object
*/
public void execute(Arena arena, MABoss boss);
}
@@ -0,0 +1,20 @@
package com.garbagemule.MobArena.waves.ability;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;;
@Retention(RetentionPolicy.RUNTIME)
public @interface AbilityInfo
{
/**
* The "pretty print" name of the ability.
* This value is printed when a boss executes the ability in the arena.
*/
public String name();
/**
* The config aliases for the ability.
* This is used by MobArena to parse ability names from the config-file.
*/
public String[] aliases();
}
@@ -0,0 +1,279 @@
package com.garbagemule.MobArena.waves.ability;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import com.garbagemule.MobArena.Messenger;
import com.garbagemule.MobArena.util.FileUtils;
public class AbilityManager
{
private static final String jarpath = "." + File.separator + "plugins" + File.separator + "MobArena.jar";
private static final String classpath = jarpath + ";" + System.getProperty("java.class.path");
private static Map<String,Ability> abilities;
/**
* Get an Ability by one of its aliases.
* @param name an Ability alias
* @return an Ability, if one exists with the given alias, false otherwise
*/
public static Ability fromString(String name) {
return abilities.get(name.toLowerCase().replaceAll("[-_.]", ""));
}
/**
* Load the known abilities as well as all custom abilities from
* the specified directory.
* @param dir a directory of .class (and/or .java) files
*/
public static void loadAbilities(File classDir) {
abilities = new HashMap<String,Ability>();
// Extract all of the source files.
File javaDir = new File(classDir, "src");
extractSourceFiles(javaDir);
// The custom abilities to compile will be in the 'src'-dir
compileAbilities(javaDir, classDir);
// Load all the custom abilities.
loadClasses(classDir);
}
private static void extractSourceFiles(File javaDir) {
// Only extract the files if the folder doesn't exist.
if (javaDir.exists()) return;
String path = "abilities/";
List<String> files = new ArrayList<String>();
files.add(path + "ChainLightning.java");
files.add(path + "LivingBomb.java");
files.add(path + "ObsidianBomb.java");
files.add(path + "Flood.java");
files.add(path + "WarpToPlayer.java");
files.add(path + "RootTarget.java");
files.add(path + "ShufflePositions.java");
files.add(path + "ShootArrow.java");
files.add(path + "ShootFireball.java");
files.add(path + "FireAura.java");
files.add(path + "LightningAura.java");
files.add(path + "DisorientDistant.java");
files.add(path + "DisorientNearby.java");
files.add(path + "DisorientTarget.java");
files.add(path + "PullDistant.java");
files.add(path + "PullNearby.java");
files.add(path + "PullTarget.java");
files.add(path + "ThrowDistant.java");
files.add(path + "ThrowNearby.java");
files.add(path + "ThrowTarget.java");
FileUtils.extractResources(javaDir, files.toArray(new String[0]));
}
private static void compileAbilities(File javaDir, File classDir) {
if (!javaDir.exists()) return;
// Make ready a new list of files to compile.
List<File> toCompile = getSourceFilesToCompile(javaDir, classDir);
// No files to compile?
if (toCompile.isEmpty()) {
return;
}
// Get the compiler and the file manager
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
// Generate some JavaFileObjects
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(toCompile);
// Include the MobArena.jar on the classpath, and set the destination folder.
List<String> options = Arrays.asList("-classpath", classpath, "-d", classDir.getPath());
// Set up the compilation task.
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, null, compilationUnits);
// Call the task.
task.call();
// And close the file manager.
try {
fileManager.close();
}
catch (Exception e) {}
}
private static List<File> getSourceFilesToCompile(File javaDir, File classDir) {
List<File> result = new ArrayList<File>();
if (javaDir == null || !javaDir.exists()) {
return result;
}
// Grab the array of compiled files.
File[] classFiles = classDir.listFiles();
// Go through each source file.
for (File javaFile : javaDir.listFiles()) {
// Skip if it's not a .java file.
if (!javaFile.getName().endsWith(".java")) {
Messenger.info("Found invalid ability file: " + javaFile.getName());
continue;
}
// Find the associated .class file.
File classFile = findClassFile(javaFile, classFiles);
// If the .class file is newer, we don't need to compile.
if (isClassFileNewer(javaFile, classFile)) {
continue;
}
result.add(javaFile);
}
return result;
}
private static File findClassFile(File javaFile, File[] classFiles) {
String javaFileName = javaFile.getName();
String classFileName = javaFileName.substring(0, javaFileName.lastIndexOf(".")) + ".class";
for (File classFile : classFiles) {
if (classFile.getName().equals(classFileName)) {
return classFile;
}
}
return null;
}
private static boolean isClassFileNewer(File javaFile, File classFile) {
if (classFile == null) return false;
return (classFile.lastModified() > javaFile.lastModified());
}
/**
* (Compiles and) loads all custom abilities in the given directory.
* @param dir a directory
*/
private static void loadClasses(File classDir) {
// Grab the class loader
ClassLoader loader = getLoader(classDir);
if (loader == null) return;
StringBuffer buffy = new StringBuffer();
for (File file : classDir.listFiles()) {
String filename = file.getName();
// Only load .class files.
int dot = filename.lastIndexOf(".class");
if (dot < 0) continue;
// Trim off the .class extension
String name = filename.substring(0, file.getName().lastIndexOf("."));
// And make an Ability.
Ability ability = makeAbility(loader, name);
if (ability == null) continue;
// Then load the ability into the map.
String abilityName = loadAbility(ability);
if (abilityName != null) {
buffy.append(", " + abilityName);
}
}
}
/**
* Loads an Ability into the abilities map by all of its aliases.
* @param ability an Ability
* @return the first alias of
*/
private static String loadAbility(Ability ability) {
// Grab the annotation.
AbilityInfo info = ability.getClass().getAnnotation(AbilityInfo.class);
if (info == null) return null;
// Put the command in the map with each of its aliases.
for (String name : info.aliases()) {
abilities.put(name, ability);
}
return info.aliases()[0];
}
/**
* Ask the given ClassLoader to load the ability with the given name.
* @param loader a ClassLoader
* @param name a class name
* @return an Ability, if the ClassLoader found one with the given name, null otherwise
*/
private static Ability makeAbility(ClassLoader loader, String name) {
try {
// Load the class.
Class<?> c = loader.loadClass(name);
// Create an instance of it.
Object o = c.newInstance();
// If it's an ability, return it.
if (o instanceof Ability) {
return (Ability) o;
}
}
catch (Exception e) {}
// Otherwise, return null.
return null;
}
/**
* Get a ClassLoader for the given directory.
* @param dir a directory
* @return a ClassLoader, or null
*/
private static ClassLoader getLoader(File dir) {
try {
ClassLoader loader = new URLClassLoader(new URL[] { dir.toURI().toURL() }, Ability.class.getClassLoader());
return loader;
}
catch (Exception e) {}
return null;
}
/**
* Turn an array of aliases into a comma-separated string of aliases.
* @param aliases an array of Strings
* @return a comma-separated String
*/
/*private static String aliasString(String[] aliases) {
StringBuffer buffy = new StringBuffer();
for (String a : aliases) {
buffy.append(a + ", ");
}
return buffy.substring(0, buffy.length() - 2);
}*/
}
@@ -0,0 +1,86 @@
package com.garbagemule.MobArena.waves.ability;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.bukkit.entity.Creature;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import com.garbagemule.MobArena.MAUtils;
import com.garbagemule.MobArena.framework.Arena;
public class AbilityUtils
{
public static Random random = new Random();
/**
* Get the target player of the LivingEntity if possible.
* @param the arena
* @param entity The entity whose target to get
* @param random Grab a random player if no target was found
* @return The target player, or null
*/
public static LivingEntity getTarget(Arena arena, LivingEntity entity, boolean random) {
if (entity instanceof Creature) {
LivingEntity target = ((Creature) entity).getTarget();
if (target instanceof Player) {
return target;
}
}
if (random) {
return getRandomPlayer(arena);
}
return null;
}
/**
* Get a random arena player.
* @param arena the arena
* @return a random arena player, or null if none were found
*/
public static Player getRandomPlayer(Arena arena) {
List<Player> list = new ArrayList<Player>(arena.getPlayersInArena());
if (list.isEmpty()) return null;
return list.get(random.nextInt(list.size()));
}
/**
* Get a list of nearby players
* @param arena the arena
* @param boss the boss
* @param x the 'radius' in which to grab players
* @return a list of nearby players
*/
public static List<Player> getNearbyPlayers(Arena arena, Entity boss, int x) {
List<Player> result = new ArrayList<Player>();
for (Entity e : boss.getNearbyEntities(x, x, x)) {
if (arena.getPlayersInArena().contains(e)) {
result.add((Player) e);
}
}
return result;
}
/**
* Get a list of distant players
* @param arena the arena
* @param boss the boss
* @param x the 'radius' in which to exclude players
* @return a list of distant players
*/
public static List<Player> getDistantPlayers(Arena arena, Entity boss, int x) {
List<Player> result = new ArrayList<Player>();
for (Player p : arena.getPlayersInArena()) {
if (MAUtils.distanceSquared(arena.getPlugin(), p, boss.getLocation()) > (double) (x*x)) {
result.add(p);
}
}
return result;
}
}