ProjectAres/PGM/src/main/java/tc/oc/pgm/mutation/types/EntityMutation.java

240 lines
8.0 KiB
Java

package tc.oc.pgm.mutation.types;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gson.reflect.TypeToken;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.player.PlayerSpawnEntityEvent;
import org.bukkit.inventory.EntityEquipment;
import tc.oc.commons.core.collection.WeakHashSet;
import tc.oc.pgm.events.MatchPlayerDeathEvent;
import tc.oc.pgm.events.PlayerChangePartyEvent;
import tc.oc.pgm.match.Match;
import tc.oc.pgm.match.MatchPlayer;
import javax.annotation.Nullable;
import java.time.Instant;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.stream.Stream;
import static tc.oc.commons.core.util.Optionals.cast;
/**
* A mutation module that tracks entity spawns.
*/
public class EntityMutation<E extends Entity> extends KitMutation {
final Set<E> entities;
final Map<Instant, Set<E>> entitiesByTime;
final Map<MatchPlayer, Set<E>> entitiesByPlayer;
final Map<E, MatchPlayer> playersByEntity;
public EntityMutation(Match match, boolean force) {
super(match, force);
this.entities = new WeakHashSet<>();
this.entitiesByTime = new WeakHashMap<>();
this.entitiesByPlayer = new WeakHashMap<>();
this.playersByEntity = new WeakHashMap<>();
}
/**
* Gets an immutable stream of all registered entities.
* @return stream of entities.
*/
public Stream<E> entities() {
return ImmutableSet.copyOf(entities).stream();
}
/**
* Gets an immutable stream of all registered entities
* by the time they spawned in ascending order.
* @return stream of entities.
*/
public Stream<E> entitiesByTime() {
return ImmutableMap.copyOf(entitiesByTime)
.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey(Comparator.comparing(Instant::toEpochMilli)))
.flatMap(entry -> ImmutableSet.copyOf(entry.getValue()).stream());
}
/**
* Gets an immutable stream of all registered entities
* that are owned by a player.
*
* If the given player is null, this will return entities
* with no owner.
* @param player the optional player.
* @return stream of entities.
*/
public Stream<E> entitiesByPlayer(@Nullable MatchPlayer player) {
return ImmutableSet.copyOf(entitiesByPlayer.getOrDefault(player, new HashSet<>())).stream();
}
/**
* Gets the optional owner of an entity.
* @param entity the entity to find the owner of.
* @return the optional player.
*/
public Optional<MatchPlayer> playerByEntity(Entity entity) {
return Optional.ofNullable(playersByEntity.get(entity));
}
/**
* Are entities with this spawn reason allowed to spawn?
* @param reason the spawn reason.
* @return whether this reason is allowed.
*/
public boolean allowed(CreatureSpawnEvent.SpawnReason reason) {
switch(reason) {
case NATURAL:
case DEFAULT:
case CHUNK_GEN:
case JOCKEY:
case MOUNT:
return false;
default:
return true;
}
}
/**
* Register a new spawned entity with an optional owner.
* @param entity the entity to register.
* @param owner the optional owner.
* @return the entity.
*/
public E register(E entity, @Nullable MatchPlayer owner) {
entities.add(entity);
final Instant now = match().getInstantNow();
final Set<E> byTime = entitiesByTime.getOrDefault(now, new WeakHashSet<>());
byTime.add(entity);
entitiesByTime.put(now, byTime);
if(owner != null) {
final Set<E> byPlayer = entitiesByPlayer.getOrDefault(owner, new WeakHashSet<>());
byPlayer.add(entity);
entitiesByPlayer.put(owner, byPlayer);
playersByEntity.put(entity, owner);
}
return entity;
}
/**
* Removes the entity from the world.
*
* Typically this should be {@link Entity#remove()},
* but it can also expire the entity after a couple of seconds.
* @param entity the entity to remove.
*/
public void remove(E entity) {
entity.remove();
}
/**
* Unregister and remove the given entity.
* @param entity the entity.
*/
public void despawn(E entity) {
entities.remove(entity);
playersByEntity.remove(entity);
Stream.of(entitiesByTime, entitiesByPlayer)
.flatMap(map -> ImmutableList.copyOf(map.values()).stream())
.forEach(set -> set.remove(entity));
remove(entity);
}
/**
* Unregister and remove any entities owned by the given player.
* @param player the owner of the entities.
*/
public void despawn(MatchPlayer player) {
ImmutableSet.copyOf(entitiesByPlayer.getOrDefault(player, new HashSet<>())).forEach(this::despawn);
}
/**
* Spawn an entity at the given location with no owner.
* @see #spawn(Location, Class, MatchPlayer)
* @return the entity.
*/
public E spawn(Location location, Class<E> entityClass) {
return spawn(location, entityClass, null);
}
/**
* Spawn an entity at the given location.
* @param location the location to spawn the entity.
* @param entityClass the class of the entity.
* @param owner the optional owner of the entity.
* @return the entity.
*/
public E spawn(Location location, Class<E> entityClass, @Nullable MatchPlayer owner) {
E entity = world().spawn(location, entityClass);
cast(entity, LivingEntity.class).ifPresent(living -> {
living.setCanPickupItems(false);
living.setRemoveWhenFarAway(true);
EntityEquipment equipment = living.getEquipment();
equipment.setHelmetDropChance(0);
equipment.setChestplateDropChance(0);
equipment.setLeggingsDropChance(0);
equipment.setBootsDropChance(0);
});
return register(entity, owner);
}
@EventHandler(ignoreCancelled = false, priority = EventPriority.HIGHEST)
public void onPlayerSpawnEntity(PlayerSpawnEntityEvent event) {
match().participant(event.getPlayer())
.ifPresent(player -> cast(event.getEntity(), new TypeToken<E>(){}.getRawType())
.ifPresent(entity -> {
register((E) entity, player);
event.setCancelled(false);
}));
}
@EventHandler(ignoreCancelled = false, priority = EventPriority.HIGHEST)
public void onEntitySpawn(CreatureSpawnEvent event) {
boolean allowed = allowed(event.getSpawnReason());
event.setCancelled(!allowed);
if(allowed) {
cast(event.getEntity(), new TypeToken<E>(){}.getRawType())
.filter(entity -> !playerByEntity((E) entity).isPresent())
.ifPresent(entity -> register((E) entity, null));
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onPlayerDeath(MatchPlayerDeathEvent event) {
despawn(event.getVictim());
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
public void onPartyChange(PlayerChangePartyEvent event) {
despawn(event.getPlayer());
}
@Override
public void remove(MatchPlayer player) {
despawn(player);
super.remove(player);
}
@Override
public void disable() {
entities().forEach(this::despawn);
Stream.of(entitiesByTime, entitiesByPlayer, playersByEntity).forEach(Map::clear);
super.disable();
}
}