Skip to content

Commit

Permalink
Added EntityPersistor
Browse files Browse the repository at this point in the history
EntityPersistor allows you to wrap an Entity instance with a proxy which will try to ensure persistence of the object as a valid reference
  • Loading branch information
boxbeam committed May 20, 2020
1 parent 393225a commit bff3a32
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 1 deletion.
67 changes: 67 additions & 0 deletions src/redempt/redlib/misc/EntityPersistor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package redempt.redlib.misc;

import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
* An Entity in Spigot may not persist if the entity it refers to is unloaded, then loaded again at a later time.
* This can make development very annoying, as you have to constantly check whether the Entity is still valid or not.
* EntityPersistor wraps an Entity using a proxy, and anytime a method is called on the Entity, it will check if
* the Entity instance is still valid. If it isn't, it will attempt to replace it with a valid instance by re-fetching
* the Entity from Bukkit.
*/
public class EntityPersistor {

/**
* Wraps an Entity object with a proxy which will attempt to ensure the Entity object remains valid
* even if the entity's chunk is unloaded, then loaded again. Helpful if you need a reference to an
* Entity over a long period of time which must not be broken. Note that any wrapped Entity will not
* interact with {@link Object#equals(Object)} reflexively. You must call .equals() on the Entity
* which has been wrapped, not on another Entity comparing it to this one. This could not be avoided,
* unfortunately, but as long as you are aware of that, it should work fine.
* @param entity The Entity to wrap
* @param <T> The type of the Entity
* @return The wrapped Entity
*/
public static <T extends Entity> T persist(T entity) {
Class<?> clazz = entity.getClass();
boolean foundInterface = false;
for (Class<?> iface : clazz.getInterfaces()) {
if (Entity.class.isAssignableFrom(iface)) {
clazz = iface;
foundInterface = true;
break;
}
}
if (!foundInterface) {
throw new IllegalArgumentException("The provided object cannot be wrapped!");
}
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {

private T instance = entity;

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!instance.isValid()) {
T replace = (T) Bukkit.getEntity(instance.getUniqueId());
if (replace != null) {
instance = replace;
}
}
if (method.getName().equals("equals") && method.getParameters().length == 1 && method.getParameters()[0].getName().equals("Object")) {
if (args[0] instanceof Entity) {
return ((Entity) args[0]).getUniqueId().equals(instance.getUniqueId());
}
return false;
}
return method.invoke(instance, args);
}

});
}

}
2 changes: 1 addition & 1 deletion src/redempt/redlib/misc/Hologram.java
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ private ArmorStand spawn(int line, String text) {
} else {
loc = start;
}
ArmorStand stand = (ArmorStand) loc.getWorld().spawnEntity(loc, EntityType.ARMOR_STAND);
ArmorStand stand = EntityPersistor.persist((ArmorStand) loc.getWorld().spawnEntity(loc, EntityType.ARMOR_STAND));
initiate(stand);
stand.setCustomName(text);
return stand;
Expand Down

0 comments on commit bff3a32

Please sign in to comment.