Package org.mctourney.autoreferee.listeners

Source Code of org.mctourney.autoreferee.listeners.ObjectiveTracer

package org.mctourney.autoreferee.listeners;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.collect.MapDifference;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.DoubleChest;
import org.bukkit.entity.Entity;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityChangeBlockEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.ItemDespawnEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerPickupItemEvent;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.plugin.Plugin;
import org.mctourney.autoreferee.AutoRefMatch;
import org.mctourney.autoreferee.AutoRefPlayer;
import org.mctourney.autoreferee.AutoRefTeam;
import org.mctourney.autoreferee.AutoReferee;
import org.mctourney.autoreferee.AutoRefMatch.TranscriptEvent;
import org.mctourney.autoreferee.goals.AutoRefGoal;
import org.mctourney.autoreferee.util.BlockData;
import org.mctourney.autoreferee.util.LocationUtil;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

public class ObjectiveTracer implements Listener
{
  AutoReferee plugin = null;

  public ObjectiveTracer(Plugin p)
  {
    plugin = (AutoReferee) p;
  }

  @EventHandler(priority = EventPriority.MONITOR)
  public void saveOnOpen(InventoryOpenEvent event)
  {
    HumanEntity he = event.getPlayer();
    if (!(he instanceof Player)) return;
    Player pl = (Player) he;

    AutoRefMatch match = plugin.getMatch(pl.getWorld());
    AutoRefPlayer apl = match == null ? null : match.getPlayer(pl);

    if (match == null || apl == null)
    { return; }
    if (apl.getTeam() == null)
    { return; }

    InventoryHolder holder = event.getInventory().getHolder();

    if (holder == pl)
    { apl.clearActiveInventoryInfo(); return; }

    String holderDescription;
    Location location;
    if (holder instanceof BlockState)
    {
      BlockState h = (BlockState) holder;
      location = h.getLocation();
      holderDescription = h.getType().toString();
    }
    else if (holder instanceof DoubleChest)
    {
      DoubleChest c = (DoubleChest) holder;
      location = c.getLocation();
      holderDescription = "double chest";
    }
    else if (holder instanceof Entity)
    {
      Entity e = (Entity) holder;
      location = e.getLocation();
      holderDescription = e.getType().toString();
    }
    else
    {
      holderDescription = "unknown container";
      location = pl.getLocation();
    }

    apl.setActiveInventoryInfo(apl.getCarrying(), location, holderDescription);
  }


  @EventHandler(priority = EventPriority.MONITOR)
  public void onClose(InventoryCloseEvent event)
  {
    HumanEntity he = event.getPlayer();
    if (!(he instanceof Player)) return;
    Player pl = (Player) he;

    AutoRefMatch match = plugin.getMatch(pl.getWorld());
    AutoRefPlayer apl = match == null ? null : match.getPlayer(pl);

    if (match == null || apl == null)
    { return; }
    if (apl.getTeam() == null)
    { return; }

    GoalsInventorySnapshot before = apl.getBeforeOpeningInventorySnapshot();
    GoalsInventorySnapshot snap = apl.getCarrying();

    if (before == null)
    { return; }

    MapDifference<BlockData, Integer> diff = before.getDiff(snap);
    if (diff.areEqual())
    { return; }

    GoalsInventorySnapshot droppedOff = GoalsInventorySnapshot.fromDiff(diff, true);
    GoalsInventorySnapshot pickedUp = GoalsInventorySnapshot.fromDiff(diff, false);

    if (!droppedOff.isEmpty() && !pickedUp.isEmpty())
    {
      match.addEvent(new TranscriptEvent(match,
          TranscriptEvent.EventType.OBJECTIVE_DETAIL, String.format(
          // {player} has dropped off {snap} and picked up {snap} from a {container} (@ {loc})
          "%s has dropped off %s and picked up %s from a %s (@ %s)", apl.getDisplayName(),
          droppedOff, pickedUp, apl.getInventoryDescription(),
          LocationUtil.toBlockCoords(apl.getInventoryLocation())),
          apl.getInventoryLocation(), unpack2(droppedOff, pickedUp, apl)
      ));
    }
    else if (!droppedOff.isEmpty())
    {
      match.addEvent(new TranscriptEvent(match,
          TranscriptEvent.EventType.OBJECTIVE_DETAIL, String.format(
          // {player} has dropped off {snap} in a {container} (@ {loc})
          "%s has dropped off %s in a %s (@ %s)", apl.getDisplayName(),
          droppedOff, apl.getInventoryDescription(),
          LocationUtil.toBlockCoords(apl.getInventoryLocation())),
          apl.getInventoryLocation(), unpack(droppedOff, apl)
      ));
    }
    else if (!pickedUp.isEmpty())
    {
      match.addEvent(new TranscriptEvent(match,
          TranscriptEvent.EventType.OBJECTIVE_DETAIL, String.format(
          // {player} has picked up {snap} from a {container} (@ {loc})
          "%s has picked up %s from a %s (@ %s)", apl.getDisplayName(),
          pickedUp, apl.getInventoryDescription(),
          LocationUtil.toBlockCoords(apl.getInventoryLocation())),
          apl.getInventoryLocation(), unpack(pickedUp, apl)
      ));
    }
    apl.clearActiveInventoryInfo();
  }

  @EventHandler(priority = EventPriority.MONITOR)
  public void tracePlace(BlockPlaceEvent event)
  {
    Player pl = event.getPlayer();
    Block block = event.getBlock();

    AutoRefMatch match = plugin.getMatch(block.getWorld());
    AutoRefPlayer apl = match == null ? null : match.getPlayer(pl);

    if (match == null || apl == null)
    { return; }
    if (apl.getTeam() == null)
    { return; }

    for (Map.Entry<BlockData, AutoRefGoal> entry :
        apl.getTeam().getGoalsByObjective().entrySet())
    {
      BlockData b = entry.getKey();
      AutoRefGoal g = entry.getValue();
      if (b.matchesBlock(block))
      {
        if (g.getItemStatus() != AutoRefGoal.ItemStatus.TARGET
            && g.isSatisfied(match)) {
          match.addEvent(new TranscriptEvent(match,
              TranscriptEvent.EventType.OBJECTIVE_PLACED, String.format(
              "%s has placed the %s on the Victory Monument!",
              apl.getDisplayName(), b.getDisplayName()), block.getLocation(), apl, b
          ));
        } else {
          // TranscriptEvent.ObjectiveDetailType.PLACE
          match.addEvent(new TranscriptEvent(match,
              TranscriptEvent.EventType.OBJECTIVE_DETAIL, String.format(
              // {player} has placed a {goal} block (@ {loc})
              "%s has placed a %s block (@ %s)", apl.getDisplayName(),
              b.getDisplayName(),
              LocationUtil.toBlockCoords(block.getLocation())),
              block.getLocation(), apl, b
          ));
        }
      }
    }
  }

  @EventHandler(priority = EventPriority.MONITOR)
  public void traceBreak(BlockBreakEvent event)
  {
    Player pl = event.getPlayer();
    Block block = event.getBlock();

    AutoRefMatch match = plugin.getMatch(block.getWorld());
    AutoRefPlayer apl = match == null ? null : match.getPlayer(pl);

    if (match == null || apl == null)
    { return; }
    if (apl.getTeam() == null)
    { return; }

    checkContainerBreak(block, apl.getTeam().getObjectives(), match,
        apl.getDisplayName());
    for (BlockData b : apl.getTeam().getObjectives())
    {
      if (b.matchesBlock(block))
      {
        // TranscriptEvent.ObjectiveDetailType.BREAK_PLAYER
        match.addEvent(new TranscriptEvent(match,
            TranscriptEvent.EventType.OBJECTIVE_DETAIL, String.format(
            // {player} has broken a {goal} block (@ {loc})
            "%s has broken a %s block (@ %s)", apl.getDisplayName(),
            b.getDisplayName(),
            LocationUtil.toBlockCoords(block.getLocation())), block
            .getLocation(), apl, b
        ));
      }
    }
  }

  /**
   * A set of players who have recently died, and therefore to ignore
   * PlayerDropEvents from.
   * <p/>
   * Players should only ever remain in this Set for less than 1 tick.
   */
  private final HashSet<Player> dropSkipPlayers = new HashSet<Player>();

  @EventHandler(priority = EventPriority.MONITOR)
  public void traceThrow(PlayerDropItemEvent event)
  {
    Player pl = event.getPlayer();

    if (dropSkipPlayers.contains(pl))
    { return; }

    BlockData item = new BlockData(event.getItemDrop().getItemStack());

    AutoRefMatch match = plugin.getMatch(pl.getWorld());
    AutoRefPlayer apl = match == null ? null : match.getPlayer(pl);

    if (match == null || apl == null)
    { return; }
    if (apl.getTeam() == null)
    { return; }

    for (BlockData b : apl.getTeam().getObjectives())
    {
      if (b.equals(item))
      {
        GoalsInventorySnapshot droppedItems = new GoalsInventorySnapshot(event.getItemDrop().getItemStack(), b);
        match.addEvent(new TranscriptEvent(match,
            TranscriptEvent.EventType.OBJECTIVE_DETAIL, String.format(
              // {player} has tossed {snap} (@ {loc})
              "%s has tossed %s (@ %s)", apl.getDisplayName(),
              droppedItems, LocationUtil.toBlockCoords(pl.getLocation())),
            pl.getLocation(), apl, b
        ));

        if (apl.hasActiveInventoryInfo())
        {
          apl.getBeforeOpeningInventorySnapshot().subtractInPlace(droppedItems);
        }
      }
    }
  }

  @EventHandler(priority = EventPriority.MONITOR)
  public void onDeath(PlayerDeathEvent ev)
  {
    final Player pl = ev.getEntity();

    // Ignore PlayerDropItemEvent for 1 tick
    dropSkipPlayers.add(pl);
    plugin.getServer().getScheduler().runTask(plugin, new Runnable()
    {
      public void run()
      {
        dropSkipPlayers.remove(pl);
      }
    });

    AutoRefMatch match = plugin.getMatch(pl.getWorld());
    AutoRefPlayer apl = match == null ? null : match.getPlayer(pl);

    if (match == null || apl == null)
    { return; }
    if (apl.getTeam() == null)
    { return; }

    GoalsInventorySnapshot snapshot = GoalsInventorySnapshot
        .fromItemsAndGoals(ev.getDrops(), apl.getTeam().getObjectives());

    if (snapshot.isEmpty())
    { return; }

    match.addEvent(new TranscriptEvent(
            match,
            TranscriptEvent.EventType.OBJECTIVE_DETAIL,
            // {player} has dropped {snap} when dying (@ {loc})
            String.format("%s has dropped %s when dying (@ %s)",
                apl.getDisplayName(), snapshot, LocationUtil.toBlockCoords(
                    apl.getLocation())
            ),
            apl.getLocation(), unpack(snapshot, apl, apl.getKiller())
        )
    );
    apl.clearActiveInventoryInfo();
  }

  @EventHandler(priority = EventPriority.MONITOR)
  public void tracePickup(PlayerPickupItemEvent event)
  {
    Player pl = event.getPlayer();

    if (event.getItem() == null)
    { return; }
    if (event.getItem().getItemStack() == null)
    { return; }

    BlockData item = new BlockData(event.getItem().getItemStack());

    AutoRefMatch match = plugin.getMatch(pl.getWorld());
    AutoRefPlayer apl = match == null ? null : match.getPlayer(pl);

    if (match == null || apl == null)
    { return; }
    if (apl.getTeam() == null)
    { return; }

    for (BlockData b : apl.getTeam().getObjectives())
    {
      if (b.equals(item))
      {
        GoalsInventorySnapshot pickupItems = new GoalsInventorySnapshot(event.getItem().getItemStack(), b);
        match.addEvent(new TranscriptEvent(match,
            TranscriptEvent.EventType.OBJECTIVE_DETAIL, String.format(
              // {player} has picked up {snap} (@ {loc})
              "%s has picked up %s (@ %s)", apl.getDisplayName(),
              pickupItems, LocationUtil.toBlockCoords(pl.getLocation())),
            pl.getLocation(), apl, b
        ));

        if (apl.hasActiveInventoryInfo())
        {
          apl.getBeforeOpeningInventorySnapshot().addInPlace(pickupItems);
        }

        untrackItem(event.getItem());
      }
    }
  }

  // This is done due to ordering of event handler calls - the monitor
  // listener that removes the entry might be called before us, so we fetch it
  // first
  private AutoRefPlayer currentResponsibleTntPlayer;

  @EventHandler(priority = EventPriority.HIGHEST)
  public void keepExplodeInfo(EntityExplodeEvent event)
  {
    currentResponsibleTntPlayer = (event.getEntity() == null) ? null : plugin.getTNTOwner(event
        .getEntity());
  }

  @EventHandler(priority = EventPriority.MONITOR)
  public void explosionTraces(EntityExplodeEvent event)
  {
    List<Block> blocks = event.blockList();
    if (blocks.isEmpty()) return;
    // remember, event entity can be null
    Entity cause = event.getEntity();
    AutoRefMatch match = plugin.getMatch(blocks.get(0).getWorld());
    Location loc = event.getLocation();

    if (match == null)
    { return; }

    Set<AutoRefTeam> teams = match.getTeams();
    Set<BlockData> goals = Sets.newHashSet();
    for (AutoRefTeam te : teams)
      goals.addAll(te.getObjectives());

    if (goals.isEmpty()) return; // not an objective match

    String causeStr;

    if (currentResponsibleTntPlayer != null)
    {
      causeStr = currentResponsibleTntPlayer.getDisplayName();
    }
    else if (cause != null)
    {
      causeStr = "A " + StringUtils.capitalize(cause.getType().toString().toLowerCase());
    }
    else
    {
      causeStr = "An explosion";
    }

    for (Block b : blocks)
      checkContainerBreak(b, goals, match, causeStr, "exploded");

    GoalsInventorySnapshot snap = new GoalsInventorySnapshot(blocks, goals);
    if (snap.isEmpty()) return;

    if (currentResponsibleTntPlayer != null)
    {
      // TranscriptEvent.ObjectiveDetailType.BREAK_PLAYER
      match.addEvent(new TranscriptEvent(
          match,
          TranscriptEvent.EventType.OBJECTIVE_DETAIL,
          // {entity} has exploded {snap} block(s) in {area} (@ {loc})
          String.format("%s has exploded %s block(s) in %s (@ %s)", causeStr, snap,
              getLocationDescription(loc, match), LocationUtil.toBlockCoords(loc)),
          loc, unpack(snap, currentResponsibleTntPlayer)
      ));
    }
    else
    {
      // TranscriptEvent.ObjectiveDetailType.BREAK_NONPLAYER
      match.addEvent(new TranscriptEvent(
          match,
          TranscriptEvent.EventType.OBJECTIVE_DETAIL,
          // {entity} has exploded {snap} block(s) in {area} (@ {loc})
          String.format("%s has exploded %s block(s) in %s (@ %s)", causeStr, snap,
              getLocationDescription(loc, match), LocationUtil.toBlockCoords(loc)),
          loc, unpack(snap)
      ));
    }
  }

  @EventHandler(priority = EventPriority.MONITOR)
  public void traceChange(EntityChangeBlockEvent event)
  {
    Block block = event.getBlock();
    AutoRefMatch match = plugin.getMatch(block.getWorld());
    if (match == null)
    { return; }

    Entity entity = event.getEntity();
    Location loc = block.getLocation();

    Set<AutoRefTeam> teams = teamsWithAccess(block.getLocation(), match, 0.1);
    Set<BlockData> goals = Sets.newHashSet();
    for (AutoRefTeam te : teams)
      goals.addAll(te.getObjectives());
    if (goals.isEmpty()) return; // not a block objectives match

    BlockData former = BlockData.fromBlock(block);
    BlockData after = new BlockData(event.getTo(), event.getData());

    String causeStr = "A " + StringUtils.capitalize(entity.getType().toString().toLowerCase());

    for (BlockData goal : goals)
    {
      if (goal.equals(former))
      {
        // process break
        // TranscriptEvent.ObjectiveDetailType.BREAK_NONPLAYER
        match.addEvent(new TranscriptEvent(match,
            TranscriptEvent.EventType.OBJECTIVE_DETAIL, String.format(
            // {entity} has broken a {goal} in {area} (@ {loc})
            "%s has broken a %s in %s (@ %s)", causeStr, goal,
            getLocationDescription(loc, match),
            LocationUtil.toBlockCoords(loc)), entity.getLocation(), goal
        ));
      }
      else if (goal.equals(after))
      {
        // process place
        // TranscriptEvent.ObjectiveDetailType.PLACE
        match.addEvent(new TranscriptEvent(match,
            TranscriptEvent.EventType.OBJECTIVE_DETAIL, String.format(
            // {entity} has placed a {goal} in {area} (@ {loc})
            "%s has placed a %s in %s (@ %s)", causeStr, goal,
            getLocationDescription(loc, match),
            LocationUtil.toBlockCoords(loc)), entity.getLocation(), goal
        ));

      }
    }

    if (block.getType() != Material.AIR)
    {
      // process potential break
      checkContainerBreak(block, goals, match, causeStr);
    }
  }

  @EventHandler
  public void traceDespawn(ItemDespawnEvent event)
  {
    if (event.getEntity() == null)
    { return; }
    if (event.getEntity().getItemStack() == null)
    { return; }

    Location loc = event.getEntity().getLocation();

    AutoRefMatch match = plugin.getMatch(loc.getWorld());

    if (match == null)
    { return; }

    Set<AutoRefTeam> teams = match.getTeams();
    Set<BlockData> goals = Sets.newHashSet();
    for (AutoRefTeam te : teams)
      goals.addAll(te.getObjectives());

    if (goals.isEmpty())
    { return; }

    GoalsInventorySnapshot snap = new GoalsInventorySnapshot(event.getEntity().getItemStack(), goals);

    match.addEvent(new TranscriptEvent(match,
        TranscriptEvent.EventType.OBJECTIVE_DETAIL, String.format(
          // "A {snap} item entity has EXPIRED in {area} (@ {loc})"
          "A %s item entity has EXPIRED in %s (@ %s)",
          snap, getLocationDescription(loc, match), LocationUtil.toBlockCoords(loc)
        ), loc, unpack(snap)
    ));
  }

  // Not an @EventHandler
  public void checkContainerBreak(Block block, Set<BlockData> goals, AutoRefMatch match,
      String causeStr)
  {
    checkContainerBreak(block, goals, match, causeStr, "broken");
  }

  public void checkContainerBreak(Block block, Set<BlockData> goals, AutoRefMatch match,
      String causeStr, String actionStr)
  {
    BlockState state = block.getState();
    if (state instanceof InventoryHolder)
    {
      GoalsInventorySnapshot snap = new GoalsInventorySnapshot(((InventoryHolder) state).getInventory(), goals);
      if (snap.isEmpty()) return;
      Location loc = block.getLocation();

      // TranscriptEvent.ObjectiveDetailType.CONTAINER_DEATH
      match.addEvent(new TranscriptEvent(match, TranscriptEvent.EventType.OBJECTIVE_DETAIL,
          // {entity} has {action}ed a {container}, spilling {snap} in {area} (@ {loc})
          String.format(
              "%s has %s a %s, spilling %s in %s (@ %s)", causeStr, actionStr,
              ChatColor.GOLD
                  + StringUtils.capitalize(block.getType().toString()
                  .toLowerCase()) + ChatColor.RESET, snap, ChatColor.BLUE
                  + getLocationDescription(loc, match),
              LocationUtil.toBlockCoords(loc)
          ), loc, unpack(snap)
      ));
    }
  }

  // Item entity tracker

  @SuppressWarnings("unused")
  private void trackItem(Item item)
  {
    // todo
  }

  private void untrackItem(Item item)
  {
    // todo
  }

  // Utility functions

  private Object[] unpack(GoalsInventorySnapshot snap, Object... others)
  {
    List<Object> arr = Lists.newArrayList(others);
    while (arr.contains(null))
      arr.remove(null);

    arr.addAll(snap.keySet());
    return arr.toArray();
  }

  private Object[] unpack(GoalsInventorySnapshot snap)
  {
    return Lists.newArrayList(snap.keySet()).toArray();
  }

  private Object[] unpack2(GoalsInventorySnapshot snap1, GoalsInventorySnapshot snap2, Object... others)
  {
    List<Object> arr = Lists.newArrayList(others);
    while (arr.contains(null))
      arr.remove(null);

    arr.addAll(Sets.union(snap1.keySet(), snap2.keySet()));
    return arr.toArray();
  }

  private Set<AutoRefTeam> teamsWithAccess(Location loc, AutoRefMatch match, double distance)
  {
    Set<AutoRefTeam> teams = Sets.newHashSet();
    for (AutoRefTeam team : match.getTeams())
    {
      if (team.canEnter(loc, distance))
        teams.add(team);
    }
    return teams;
  }

  private String getLocationDescription(Location loc, AutoRefMatch match)
  {
    // TODO map-defined points of interest
    Validate.isTrue(match.getWorld().equals(loc.getWorld()),
        "The provided location must be within the provided match!");
    if (match.inStartRegion(loc))
      return "the starting area";

    Set<AutoRefTeam> teamsDirect = teamsWithAccess(loc, match, 0);
    Set<AutoRefTeam> teamsNearby = teamsWithAccess(loc, match, 3);

    if (teamsDirect.isEmpty())
    {
      if (teamsNearby.isEmpty())
        return "the void lane";
      else if (teamsNearby.size() == 1)
        return "the void lane nearby " + teamsNearby.iterator().next().getDisplayName()
            + ChatColor.RESET;
      else
        return "the void lane nearby the shared area";
    }
    else if (teamsDirect.size() == 1)
      // XXX is this the coloring we want?
      // note that this is what will happen most of the time
      return teamsDirect.iterator().next().getDisplayName() + "'s lane" + ChatColor.RESET;
    else
    {
      return "the shared area"; // TODO improve for modes where both teams share the whole map
    }
  }
}
TOP

Related Classes of org.mctourney.autoreferee.listeners.ObjectiveTracer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.