/*
* This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT).
*
* Copyright (c) JCThePants (www.jcwhatever.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.jcwhatever.nucleus.internal;
import com.jcwhatever.nucleus.Nucleus;
import com.jcwhatever.nucleus.collections.WeakHashSet;
import com.jcwhatever.nucleus.managed.leash.ILeashTracker;
import com.jcwhatever.nucleus.utils.PreCon;
import com.jcwhatever.nucleus.utils.player.PlayerUtils;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.PlayerLeashEntityEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerUnleashEntityEvent;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.WeakHashMap;
/**
* Internal implementation of {@link ILeashTracker}.
*/
public class InternalLeashTracker implements ILeashTracker, Listener {
private final Map<UUID, Set<Entity>> _playerMap = new HashMap<>(35);
private final Map<Entity, UUID> _entityMap = new WeakHashMap<>(55);
public InternalLeashTracker() {
Bukkit.getPluginManager().registerEvents(this, Nucleus.getPlugin());
}
@Override
public Collection<Entity> getLeashed(Player player) {
return getLeashed(player, new ArrayList<Entity>(10));
}
@Override
public <T extends Collection<Entity>> T getLeashed(Player player, T output) {
PreCon.notNull(player);
PreCon.notNull(output);
synchronized (_playerMap) {
Set<Entity> entities = _playerMap.get(player.getUniqueId());
if (entities == null) {
Chunk chunk = player.getLocation().getChunk();
Entity[] entityArray = chunk.getEntities();
entities = new HashSet<>(7);
for (Entity entity : entityArray) {
if (!(entity instanceof LivingEntity) || entity instanceof Player)
continue;
if (entity.isDead() || !entity.isValid())
continue;
LivingEntity living = (LivingEntity)entity;
if (living.isLeashed() && living.getLeashHolder().equals(player)) {
entities.add(living);
}
}
_playerMap.put(player.getUniqueId(), entities);
}
Iterator<Entity> iterator = entities.iterator();
while (iterator.hasNext()) {
LivingEntity entity = (LivingEntity)iterator.next();
if (entity.isDead() || !entity.isValid() || !player.equals(entity.getLeashHolder())) {
iterator.remove();
}
else {
output.add(entity);
}
}
}
return output;
}
@Override
@Nullable
public Player getLeashedTo(Entity entity) {
UUID playerId = _entityMap.get(entity);
return PlayerUtils.getPlayer(playerId);
}
@Override
public boolean registerLeash(Player player, Entity leashed) {
if (!(leashed instanceof LivingEntity))
return false;
if (!player.equals(((LivingEntity) leashed).getLeashHolder()))
return false;
Set<Entity> set = _playerMap.get(player.getUniqueId());
if (set == null) {
set = new HashSet<>(10);
_playerMap.put(player.getUniqueId(), set);
}
set.add(leashed);
_entityMap.put(leashed, player.getUniqueId());
return true;
}
@EventHandler(priority = EventPriority.LOWEST)
private void onPlayerQuit(PlayerQuitEvent event) {
Set<Entity> set = _playerMap.remove(event.getPlayer().getUniqueId());
if (set == null)
return;
for (Entity entity : set) {
_entityMap.remove(entity);
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
private void onPlayerLeash(PlayerLeashEntityEvent event) {
UUID playerId = event.getPlayer().getUniqueId();
Entity leashHolder = event.getLeashHolder();
if (event.getPlayer().equals(leashHolder))
addPlayerLeash(playerId, event.getEntity());
else {
removePlayerLeash(playerId, event.getEntity());
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
private void onPlayerUnleash(PlayerUnleashEntityEvent event) {
removePlayerLeash(event.getPlayer().getUniqueId(), event.getEntity());
}
@EventHandler(priority = EventPriority.MONITOR)
private void onDeath(EntityDeathEvent event) {
// check for "cancelled" event
if (event.getEntity().getHealth() > 0)
return;
UUID playerId = _entityMap.remove(event.getEntity());
if (playerId == null)
return;
_playerMap.remove(playerId);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
private void onCreatureSpawn(CreatureSpawnEvent event) {
if (!event.getEntity().isLeashed())
return;
Entity leashHolder = event.getEntity().getLeashHolder();
if (leashHolder instanceof Player) {
addPlayerLeash(leashHolder.getUniqueId(), event.getEntity());
}
}
private void addPlayerLeash(UUID playerId, Entity leashed) {
synchronized (_playerMap) {
Set<Entity> set = _playerMap.get(playerId);
if (set == null) {
set = new WeakHashSet<>(5);
_playerMap.put(playerId, set);
}
set.add(leashed);
}
}
private void removePlayerLeash(UUID playerId, Entity unleashed) {
synchronized (_playerMap) {
Set<Entity> set = _playerMap.get(playerId);
if (set == null)
return;
set.remove(unleashed);
}
}
}