1
mirror of https://github.com/StarWishsama/Slimefun4.git synced 2024-09-20 03:35:51 +00:00

Cache entity names

This commit is contained in:
TheBusyBiscuit 2021-01-15 01:32:39 +01:00
parent 34f24f4d60
commit 570908a448
5 changed files with 256 additions and 172 deletions

View File

@ -1,11 +1,9 @@
package io.github.thebusybiscuit.slimefun4.core.attributes;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.ArmorStand;
import org.bukkit.util.Vector;
import io.github.thebusybiscuit.cscorelib2.chat.ChatColors;
@ -35,17 +33,7 @@ public interface HologramOwner extends ItemAttribute {
*/
default void updateHologram(@Nonnull Block b, @Nonnull String text) {
Location loc = b.getLocation().add(getHologramOffset());
SlimefunPlugin.getHologramsService().updateHologram(loc, hologram -> {
hologram.setCustomName(ChatColors.color(text));
hologram.setCustomNameVisible(true);
});
}
@Nullable
default ArmorStand getHologram(@Nonnull Block b, boolean createIfNoneExists) {
Location loc = b.getLocation().add(getHologramOffset());
return SlimefunPlugin.getHologramsService().getHologram(loc, createIfNoneExists);
SlimefunPlugin.getHologramsService().setHologramLabel(loc, ChatColors.color(text));
}
/**

View File

@ -1,79 +0,0 @@
package io.github.thebusybiscuit.slimefun4.core.services.holograms;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.bukkit.Bukkit;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
/**
* This represents an {@link ArmorStand} or hologram that can expire.
*
* @author TheBusyBiscuit
*
*/
class CachedArmorStand {
/**
* This is the minimum duration after which the {@link CachedArmorStand} will expire.
*/
private static final long EXPIRES_AFTER = TimeUnit.MINUTES.toMillis(10);
/**
* The {@link UUID} of the {@link ArmorStand}.
*/
private final UUID uniqueId;
/**
* The timestamp of when the {@link ArmorStand} was last accessed.
*/
private long lastAccess;
/**
* This creates a new {@link CachedArmorStand} for the given {@link UUID}.
*
* @param uniqueId
* The {@link UUID} of the corresponding {@link ArmorStand}
*/
public CachedArmorStand(@Nonnull UUID uniqueId) {
this.uniqueId = uniqueId;
this.lastAccess = System.currentTimeMillis();
}
/**
* This returns the corresponding {@link ArmorStand}
* and also updates the "lastAccess" timestamp.
* <p>
* If the {@link ArmorStand} was removed, it will return null.
*
* @return The {@link ArmorStand} or null.
*/
@Nullable
public ArmorStand getArmorStand() {
Entity n = Bukkit.getEntity(uniqueId);
if (n instanceof ArmorStand && n.isValid()) {
this.lastAccess = System.currentTimeMillis();
return (ArmorStand) n;
} else {
this.lastAccess = 0;
return null;
}
}
/**
* This returns whether this {@link CachedArmorStand} has expired.
* The armorstand will expire if the last access has been more than 10
* minutes ago.
*
* @return Whether this {@link CachedArmorStand} has expired
*/
public boolean isExpired() {
return System.currentTimeMillis() - lastAccess > EXPIRES_AFTER;
}
}

View File

@ -0,0 +1,136 @@
package io.github.thebusybiscuit.slimefun4.core.services.holograms;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.bukkit.Bukkit;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import com.google.common.base.Objects;
/**
* This represents an {@link ArmorStand} that can expire and be renamed.
*
* @author TheBusyBiscuit
*
*/
class Hologram {
/**
* This is the minimum duration after which the {@link Hologram} will expire.
*/
private static final long EXPIRES_AFTER = TimeUnit.MINUTES.toMillis(10);
/**
* The {@link UUID} of the {@link ArmorStand}.
*/
private final UUID uniqueId;
/**
* The timestamp of when the {@link ArmorStand} was last accessed.
*/
private long lastAccess;
/**
* The label of this {@link Hologram}.
*/
private String label;
/**
* This creates a new {@link Hologram} for the given {@link UUID}.
*
* @param uniqueId
* The {@link UUID} of the corresponding {@link ArmorStand}
*/
Hologram(@Nonnull UUID uniqueId) {
this.uniqueId = uniqueId;
this.lastAccess = System.currentTimeMillis();
}
/**
* This returns the corresponding {@link ArmorStand}
* and also updates the "lastAccess" timestamp.
* <p>
* If the {@link ArmorStand} was removed, it will return null.
*
* @return The {@link ArmorStand} or null.
*/
@Nullable
ArmorStand getArmorStand() {
Entity n = Bukkit.getEntity(uniqueId);
if (n instanceof ArmorStand && n.isValid()) {
this.lastAccess = System.currentTimeMillis();
return (ArmorStand) n;
} else {
this.lastAccess = 0;
return null;
}
}
/**
* This checks if the associated {@link ArmorStand} has despawned.
*
* @return Whether the {@link ArmorStand} despawned
*/
boolean hasDespawned() {
return getArmorStand() == null;
}
/**
* This returns whether this {@link Hologram} has expired.
* The armorstand will expire if the last access has been more than 10
* minutes ago.
*
* @return Whether this {@link Hologram} has expired
*/
boolean hasExpired() {
return System.currentTimeMillis() - lastAccess > EXPIRES_AFTER;
}
/**
* This method sets the label of this {@link Hologram}.
*
* @param label
* The label to set
*/
void setLabel(@Nullable String label) {
if (Objects.equal(this.label, label)) {
/*
* Label is already set, no need to cause an entity
* update. But we can update the lastAccess flag.
*/
this.lastAccess = System.currentTimeMillis();
} else {
this.label = label;
ArmorStand entity = getArmorStand();
if (entity != null) {
if (label != null) {
entity.setCustomNameVisible(true);
entity.setCustomName(label);
} else {
entity.setCustomNameVisible(false);
entity.setCustomName(null);
}
}
}
}
/**
* This will remove the {@link ArmorStand} and expire this {@link Hologram}.
*/
void remove() {
ArmorStand armorstand = getArmorStand();
if (armorstand != null) {
lastAccess = 0;
armorstand.remove();
}
}
}

View File

@ -27,7 +27,6 @@ import io.github.thebusybiscuit.slimefun4.implementation.SlimefunPlugin;
/**
* This service is responsible for handling holograms.
* This includes error management when something goes wrong.
*
* @author TheBusyBiscuit
*
@ -59,7 +58,7 @@ public class HologramsService {
/**
* Our cache to save {@link Entity} lookups
*/
private final Map<BlockPosition, CachedArmorStand> cache = new HashMap<>();
private final Map<BlockPosition, Hologram> cache = new HashMap<>();
/**
* This constructs a new {@link HologramsService}.
@ -83,20 +82,20 @@ public class HologramsService {
}
/**
* This purges all expired {@link CachedArmorStand CachedArmorStands}.
* This purges all expired {@link Hologram CachedArmorStands}.
*/
private void purge() {
Iterator<CachedArmorStand> iterator = cache.values().iterator();
Iterator<Hologram> iterator = cache.values().iterator();
while (iterator.hasNext()) {
if (iterator.next().isExpired()) {
if (iterator.next().hasExpired()) {
iterator.remove();
}
}
}
/**
* This returns the hologram associated with the given {@link Location}.
* This returns the {@link Hologram} associated with the given {@link Location}.
* If createIfNoneExists is set to true a new {@link ArmorStand} will be spawned
* if no existing one could be found.
*
@ -108,24 +107,20 @@ public class HologramsService {
* @return The existing (or newly created) hologram
*/
@Nullable
public ArmorStand getHologram(@Nonnull Location loc, boolean createIfNoneExists) {
private Hologram getHologram(@Nonnull Location loc, boolean createIfNoneExists) {
Validate.notNull(loc, "Location cannot be null");
// Make sure there's no concurrency issues
if (!Bukkit.isPrimaryThread()) {
throw new UnsupportedOperationException("A hologram cannot be accessed asynchronously.");
}
BlockPosition position = new BlockPosition(loc);
CachedArmorStand cachedEntity = cache.get(position);
Hologram hologram = cache.get(position);
// Check if the ArmorStand was cached
if (cachedEntity != null) {
ArmorStand armorstand = cachedEntity.getArmorStand();
// If the Entity still exists, return it
if (armorstand != null) {
return armorstand;
}
// Check if the ArmorStand was cached and still exists
if (hologram != null && hologram.hasDespawned()) {
return hologram;
}
// Scan all nearby entities which could be possible holograms
@ -138,52 +133,134 @@ public class HologramsService {
if (container.has(persistentDataKey, PersistentDataType.LONG)) {
// Check if it is ours or a different one.
if (container.get(persistentDataKey, PersistentDataType.LONG).equals(position.getPosition())) {
return (ArmorStand) n;
return getAsHologram(position, n, container);
}
} else {
// Set a persistent tag to re-identify the correct hologram later
container.set(persistentDataKey, PersistentDataType.LONG, position.getPosition());
return (ArmorStand) n;
return getAsHologram(position, n, container);
}
}
}
if (createIfNoneExists) {
// Spawn a new ArmorStand
ArmorStand hologram = (ArmorStand) loc.getWorld().spawnEntity(loc, EntityType.ARMOR_STAND);
ArmorStand armorstand = (ArmorStand) loc.getWorld().spawnEntity(loc, EntityType.ARMOR_STAND);
PersistentDataContainer container = armorstand.getPersistentDataContainer();
// Set a persistent tag to re-identify the correct hologram later
PersistentDataContainer container = hologram.getPersistentDataContainer();
container.set(persistentDataKey, PersistentDataType.LONG, position.getPosition());
hologram.setInvisible(true);
hologram.setInvulnerable(true);
hologram.setSilent(true);
hologram.setMarker(true);
hologram.setAI(false);
hologram.setGravity(false);
return hologram;
return getAsHologram(position, armorstand, container);
} else {
return null;
}
}
/**
* This removes the hologram at that given {@link Location}.
* This checks if a given {@link Entity} is an {@link ArmorStand}
* and whether it has the correct attributes to be considered a {@link Hologram}.
*
* @param n
* The {@link Entity} to check
*
* @return Whether this could be a hologram
*/
private boolean isHologram(@Nonnull Entity n) {
if (n instanceof ArmorStand) {
ArmorStand armorstand = (ArmorStand) n;
// The absolute minimum requirements to count as a hologram
return !armorstand.isVisible() && armorstand.isSilent() && !armorstand.hasGravity();
} else {
return false;
}
}
/**
* This will cast the {@link Entity} to an {@link ArmorStand} and it will apply
* all necessary attributes to the {@link ArmorStand}, then return a {@link Hologram}.
*
* @param position
* The {@link BlockPosition} of this hologram
* @param entity
* The {@link Entity}
* @param container
* The {@link PersistentDataContainer} of the given {@link Entity}
*
* @return The {@link Hologram}
*/
@Nullable
private Hologram getAsHologram(@Nonnull BlockPosition position, @Nonnull Entity entity, @Nonnull PersistentDataContainer container) {
if (entity instanceof ArmorStand) {
ArmorStand armorstand = (ArmorStand) entity;
armorstand.setVisible(false);
armorstand.setInvulnerable(true);
armorstand.setSilent(true);
armorstand.setMarker(true);
armorstand.setAI(false);
armorstand.setGravity(false);
armorstand.setRemoveWhenFarAway(false);
// Set a persistent tag to re-identify the correct hologram later
container.set(persistentDataKey, PersistentDataType.LONG, position.getPosition());
// Store in cache for faster access
Hologram hologram = new Hologram(armorstand.getUniqueId());
cache.put(position, hologram);
return hologram;
} else {
// This should never be reached
return null;
}
}
/**
* This updates the {@link Hologram}.
* You can use it to set the nametag or other properties.
* <p>
* <strong>This method must be executed on the main {@link Server} {@link Thread}.</strong>
*
* @param loc
* The {@link Location}
* @param consumer
* The callback to run
*/
private void updateHologram(@Nonnull Location loc, @Nonnull Consumer<Hologram> consumer) {
Validate.notNull(loc, "Location must not be null");
Validate.notNull(consumer, "Callbacks must not be null");
Runnable runnable = () -> {
Hologram hologram = getHologram(loc, true);
if (hologram != null) {
consumer.accept(hologram);
}
};
if (Bukkit.isPrimaryThread()) {
runnable.run();
} else {
SlimefunPlugin.runSync(runnable);
}
}
/**
* This removes the {@link Hologram} at that given {@link Location}.
* <p>
* <strong>This method must be executed on the main {@link Server} {@link Thread}.</strong>
*
* @param loc
* The {@link Location}
*
* @return Whether the hologram could be removed, false if the hologram does not exist or was already removed
* @return Whether the {@link Hologram} could be removed, false if the {@link Hologram} does not exist or was
* already
* removed
*/
public boolean removeHologram(@Nonnull Location loc) {
Validate.notNull(loc, "Location cannot be null");
if (Bukkit.isPrimaryThread()) {
ArmorStand hologram = getHologram(loc, false);
Hologram hologram = getHologram(loc, false);
if (hologram != null) {
hologram.remove();
@ -197,52 +274,17 @@ public class HologramsService {
}
/**
* This updates the hologram.
* You can use it to set the nametag or other properties.
* <p>
* <strong>This method must be executed on the main {@link Server} {@link Thread}.</strong>
* This will update the label of the {@link Hologram}.
*
* @param loc
* The {@link Location}
* @param consumer
* The callback to run
* The {@link Location} of this {@link Hologram}
* @param label
* The label to set, can be null
*/
public void updateHologram(@Nonnull Location loc, @Nonnull Consumer<ArmorStand> consumer) {
public void setHologramLabel(@Nonnull Location loc, @Nullable String label) {
Validate.notNull(loc, "Location must not be null");
Validate.notNull(consumer, "Callbacks must not be null");
if (Bukkit.isPrimaryThread()) {
consumer.accept(getHologram(loc, true));
} else {
SlimefunPlugin.runSync(() -> consumer.accept(getHologram(loc, true)));
}
}
/**
* This checks if a given {@link Entity} is an {@link ArmorStand}
* and whether it has the correct attributes to be considered a hologram.
*
* @param n
* The {@link Entity} to check
*
* @return Whether this could be a hologram
*/
private boolean isHologram(@Nonnull Entity n) {
if (n instanceof ArmorStand) {
ArmorStand armorstand = (ArmorStand) n;
// @formatter:off
return armorstand.isValid()
&& armorstand.isInvisible()
&& armorstand.isInvulnerable()
&& armorstand.isSilent()
&& armorstand.isMarker()
&& !armorstand.hasAI()
&& !armorstand.hasGravity();
// @formatter:on
} else {
return false;
}
updateHologram(loc, hologram -> hologram.setLabel(label));
}
}

View File

@ -5,7 +5,6 @@ import javax.annotation.ParametersAreNonnullByDefault;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.ItemStack;
@ -43,10 +42,8 @@ public abstract class NetherStarReactor extends Reactor {
@Override
public void extraTick(@Nonnull Location l) {
SlimefunPlugin.runSync(() -> {
ArmorStand hologram = getHologram(l.getBlock(), true);
for (Entity entity : hologram.getNearbyEntities(5, 5, 5)) {
if (entity instanceof LivingEntity && entity.isValid()) {
for (Entity entity : l.getWorld().getNearbyEntities(l, 5, 5, 5, n -> n instanceof LivingEntity && n.isValid())) {
if (entity instanceof LivingEntity) {
((LivingEntity) entity).addPotionEffect(new PotionEffect(PotionEffectType.WITHER, 60, 1));
}
}