package tc.oc.pgm.tracker.trackers; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; import javax.annotation.Nullable; import javax.inject.Inject; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPistonEvent; import org.bukkit.event.block.BlockPistonExtendEvent; import org.bukkit.event.block.BlockPistonRetractEvent; import tc.oc.commons.core.logging.Loggers; import tc.oc.pgm.events.BlockTransformEvent; import tc.oc.pgm.events.ListenerScope; import tc.oc.pgm.match.MatchScope; import tc.oc.pgm.match.ParticipantState; import tc.oc.pgm.tracker.BlockResolver; import tc.oc.pgm.tracker.damage.BlockInfo; import tc.oc.pgm.tracker.damage.OwnerInfo; import tc.oc.pgm.tracker.damage.PhysicalInfo; import tc.oc.pgm.tracker.damage.TrackerInfo; import static com.google.common.base.Preconditions.checkNotNull; /** * Tracks the ownership of {@link Block}s and resolves damage caused by them */ @ListenerScope(MatchScope.RUNNING) public class BlockTracker implements BlockResolver, Listener { private final Logger logger; private final Map<Block, TrackerInfo> blocks = new HashMap<>(); private final Map<Block, Material> materials = new HashMap<>(); @Inject BlockTracker(Loggers loggers) { this.logger = loggers.get(getClass()); } @Override public PhysicalInfo resolveBlock(Block block) { TrackerInfo info = blocks.get(block); if(info instanceof PhysicalInfo) { return (PhysicalInfo) info; } else if(info instanceof OwnerInfo) { return new BlockInfo(block.getState(), ((OwnerInfo) info).getOwner()); } else { return new BlockInfo(block.getState()); } } @Override public @Nullable TrackerInfo resolveInfo(Block block) { return blocks.get(block); } @Override public @Nullable ParticipantState getOwner(Block block) { OwnerInfo info = resolveInfo(block, OwnerInfo.class); return info == null ? null : info.getOwner(); } public void trackBlockState(Block block, @Nullable Material material, @Nullable TrackerInfo info) { checkNotNull(block); if(info != null) { blocks.put(block, info); if(material != null) { materials.put(block, material); } else { materials.remove(block); } logger.fine("Track block=" + block + " material=" + material + " info=" + info); } else { clearBlock(block); } } public void trackBlockState(BlockState state, @Nullable TrackerInfo info) { checkNotNull(state); trackBlockState(state.getBlock(), state.getMaterial(), info); } public void clearBlock(Block block) { checkNotNull(block); blocks.remove(block); materials.remove(block); logger.fine("Clear block=" + block); } boolean isPlaced(BlockState state) { // If block was registered with a specific material, check that the new state // has the same material, otherwise assume the block is still placed. Material material = materials.get(state.getBlock()); return material == null || material == state.getMaterial(); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onTransform(BlockTransformEvent event) { if(event.getCause() instanceof BlockPistonEvent) return; Block block = event.getOldState().getBlock(); TrackerInfo info = blocks.get(block); if(info != null && !isPlaced(event.getNewState())) { clearBlock(block); } } private void handleMove(Collection<Block> blocks, BlockFace direction) { Map<Block, TrackerInfo> keepInfo = new HashMap<>(); Map<Block, Material> keepMaterials = new HashMap<>(); List<Block> remove = new ArrayList<>(); for(Block block : blocks) { TrackerInfo info = this.blocks.get(block); if(info != null) { remove.add(block); keepInfo.put(block.getRelative(direction), info); Material material = materials.get(block); if(material != null) { keepMaterials.put(block, material); } } } for(Block block : remove) { TrackerInfo info = keepInfo.remove(block); if(info != null) { this.blocks.put(block, info); Material material = keepMaterials.get(block); if(material != null) { this.materials.put(block, material); } } else { this.blocks.remove(block); this.materials.remove(block); } } this.blocks.putAll(keepInfo); this.materials.putAll(keepMaterials); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPistonExtend(BlockPistonExtendEvent event) { handleMove(event.getBlocks(), event.getDirection()); } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onPistonRetract(BlockPistonRetractEvent event) { handleMove(event.getBlocks(), event.getDirection()); } }