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:
parent
34f24f4d60
commit
570908a448
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user