@ThreadSafe public final class WeakEntity<T extends Entity> extends Object
Three constructors are provided. In the case that the stored value comes from an alien-source, or any
general situation where the value to be held by the WeakEntity is unknown and unspecified, the recommended factory
method to use is orEmpty(net.tridentsdk.entity.Entity)
. This prevents the creation of unnecessary instances
of WeakEntity where the value is always going to null
. Because this method always returns the same instance
of a null
holding WeakEntity, registration does not need to be executed, where registration means to place
the reference into a queue which will be cleared once the entity has been removed. In this context, and alien method
is: "A method you call for which you have no control over the code, and further don’t even know what the code
does, other than the method signature [...] it could refer to calls to third party libraries."
Because entities are loaded with all of their data and AI, the removal of them entirely depends on the references left held to it. If the entity is technically removed by the server, but still held by a plugin, for example, then a memory leak will occur because the entity will never actually be removed. It is not updated for players, but the reference stays in memory, wasting computing power on a non-existent entity.
Use this class to store a link or bridge to the reference of an entity. The storage of an instance of this class
will only hold this class only, and the reference to the entity in this class may become null
at any time,
in which case is indicated by isNull()
returning true
.
Much thought was put into the design of this class, especially if it should be made thread-safe. Because this is an internal change, it can be easily done without breaking the API. However, the decision was made to make this class thread-safe in order to preserve the fact that both the users of this API, including plugins and the server itself is concurrent. It would decrease the burden to handle this safely using the internal implementation rather than having the client code execute awkward constructs in order to keep consistency. This is OK for the implementation to perform, but because plugin authors may not always think about the concurrent nature of this API, it was done at the cost of performance to reduce bugs which are often difficult to find.
This is the equivalent of Optional
but for entities specifically, much like an
optional which stores a WeakReference
of the specified property. While this class does use
WeakReference to manage the references, other methods to safeguard the reference is also implemented, such as
removal
listeners.
Internally, this class manages a queue of references which, in turn, are polled by a thread called
Trident - Reference Handler
. Every call to clear a specific reference also runs the mark/sweep collector,
although it is invoked directly rather than using runMarkSweep()
. The implementation of the collector may
be
found in the documentation for runMarkSweep()
. Entities call
clearReferencesTo(net.tridentsdk.entity.Entity)
when they are removed to purge old references to that
entity
after it has finished despawning. This method also runs the mark/sweep collector. This is only implemented as a
safeguard behind WeakReference
, which is used to store the actual instance of the entity to be
weakly referenced. The reference handling thread cleans up the entity after all the references have been cleared, as
well as collecting any other obsolete references. This was done because WeakReference may allow the reference to be
held longer than it has lived, in the case that the GC does not run, the reference handler will perform the
necessary actions to make sure the entity is correctly nulled.
Here are examples on the usage of this class.
WeakEntity can and should be used when storing entities, such as players inside a collection or reference.
private final Map<WeakEntity<Player>, Integer> score =
Maps.newHashMap();
// Add player
Player player = ...
int playerScore = ...
score.put(WeakEntity.of(player), playerScore);
// Checking player
for (WeakEntity<Player> weakEntity : score.keySet()) {
// This is simply proof of concept, don't check if it is null
// then try to obtain it
// Or try to hide the player from himself/herself
if (weakEntity.isNull())
continue;
weakEntity.obtain().hide(weakEntity);
}
WeakEntity acts like an optional which can be used to eliminate the need to check for null
.
// Finding the player
// Don't do this in production-stage code, it doesn't work the way the name implies!
public WeakEntity<Bat> findClosest(Player player, int range) {
for (Entity entity : player.withinRange(range)) {
if (entity instanceof Bat) {
// Unable to be null, so we use of(Entity)
// Instead of orEmpty(Entity)
return WeakEntity.of(entity);
}
}
// No bats near the player, it's empty
return WeakEntity.empty();
}
// Find a Bat closest to the player, or spawn a new one, and accelerate it upwards (just because)
findClosest(player, 69)
.or(EntityBuilder.create().build(TridentBat.class))
.setVelocity(new Vector(0, 10, 0));
WeakEntity does not need to be instantiated to find in a collection.
Map<WeakEntity<Entity>, Object> map = Maps.newHashMap();
...
Object o = map.get(WeakEntity.searchFor(entity));
Finally, WeakEntity can be used to find and prevent bugs which would otherwise cause NullPointerExceptions.
// Using it to find bugs
// Oh, leave it here for a while until I implement a different method, then I'll come back to it
WeakEntity<Player> weakEntity = WeakEntity.of(null);
...
// I was smart and used obtain() instead of entity()
weakEntity.obtain().hide();
// Code errors
// Go back to the line you found in the stacktrace
// Looks like it was on the line that tried to obtain the entity
// from a WeakEntity
// We remember that we implemented a find player method later on
// Final code
WeakEntity<Player> weakEntity = WeakEntity.of(player());
Modifier and Type | Class and Description |
---|---|
static class |
WeakEntity.CleaningIterator<E>
An iterator for a collection holding
WeakEntity s. |
Modifier and Type | Method and Description |
---|---|
void |
clear()
This removes the reference to the entity in this particular instance of WeakEntity
|
static void |
clearReferencesTo(Entity entity)
Removes the references of the entity from any WeakEntity instances which are non-null
|
static <T extends Entity> |
empty()
Obtains a WeakEntity which holds a
null referenced entity |
T |
entity()
Obtains the instance of the referenced entity, or
null if it has been dereferenced |
boolean |
equals(Object obj) |
Object |
finder()
Instance method of
searchFor(net.tridentsdk.entity.Entity) . |
int |
hashCode() |
boolean |
isNull()
Observes the reference to see if the entity is
null or not accessible via the server anymore |
static <E> WeakEntity.CleaningIterator<E> |
iterate(Collection<E> collect)
Obtains an iterator which cleans elements in a collection which holds
WeakEntity
instances |
T |
obtain()
Obtains the referenced entity, but is fail-fast
|
static <T extends Entity> |
of(T referencedEntity)
Obtains a WeakEntity which holds the specified entity
|
T |
or(T fallback)
Obtains the instance of the referenced entity, or, if it
isNull() , then this method will return the
specified fallback object, which must match the type of this object |
static <T extends Entity> |
orEmpty(T referencedEntity)
Obtains a WeakEntity which is either a
empty() reference or a reference
of(net.tridentsdk.entity.Entity) , depending on if the specified entity is null or
non-null , respectively |
static void |
runMarkSweep()
Forces the reference handler thread to run the
null or garbage reference collection cycle and reclaim
memory lost by those references |
static Object |
searchFor(Entity entity)
Provides an object which possesses the matching properties for a WeakEntity
|
String |
toString() |
public static <T extends Entity> WeakEntity<T> empty()
null
referenced entity
This method does not produce a new object. The instance of an empty WeakEntity is held in a static
field inside this class.
T
- the type of entity to hold null
fornull
public static <T extends Entity> WeakEntity<T> of(T referencedEntity)
The entity which is specified will become null
when it is dereferenced from the server.
This may be used in the place of orEmpty(net.tridentsdk.entity.Entity)
if creation of a new
WeakEntity for a null
entity is OK. In other words, this is a fast-path (albeit the fact that the
difference in performance is negligible in comparison with creation overhead and memory) for the
orEmpty(net.tridentsdk.entity.Entity)
.
T
- the type of entity to holdreferencedEntity
- the entity to hold until it is removednull
public static <T extends Entity> WeakEntity<T> orEmpty(T referencedEntity)
empty()
reference or a reference
of(net.tridentsdk.entity.Entity)
, depending on if the specified entity is null
or
non-null
, respectively
If the returned WeakEntity holds null
, and is equivalent to empty()
, then no instance of
WeakEntity was created.
If the referenced entity is known to be null
, then do not use this method. Although it is
still correct, if it is not possible for it to be null
, use an empty()
WeakEntity.
T
- the type of entity to holdreferencedEntity
- the entity to obtain the reference forempty()
if the specified entity to reference
is null
@InternalUseOnly public static void clearReferencesTo(Entity entity) throws IllegalAccessException
This method was meant to be used only for the implementation. The calling of this method by a non-Trident class has no effect.
entity
- the entity to dereference and clear from WeakEntityIllegalAccessException
- when the caller is not Tridentpublic static Object searchFor(Entity entity)
This is needed when obtaining a WeakEntity from a collection. The returned object overrides equals and
hashCode, as well as toString to mimic the actual referenced entity. If the reference is null
, then no
new object is created and the method returns a cached version of the object. Because the WeakEntity's original
implementation also delegates the same identifying functions to the reference, this is the preferred way to find
a WeakEntity from a collection or to match with a stored version.
This method never returns null. If there is no entity which is known to exist with a WeakEntity, then the
returned object searchFor(null).equals(null)
.
entity
- the entity to get the finder forpublic static <E> WeakEntity.CleaningIterator<E> iterate(Collection<E> collect)
WeakEntity
instances
This method is recommended for usage with any collection which holds instances of
WeakEntity
, as although it may reduce memory leaks caused by resources being held
after a referencing class is not fully unloaded, one WeakEntity is referenced is several places internally.
This supports all collections that are part of the Java Collections Framework. In particular, collections
which have 2 keys and obtain entry sets with a WeakEntity
in the key, value, or both
are supported by this iterator.
Normal use case:
List<WeakEntity<Player>> list = Lists.newArrayList();
for (WeakEntity<Player> weakEntity : WeakEntity.iterate(list)) {
// Obtain can be used here
}
No support is provided for iteration-then-remove, as the semantics of the iterator precede checks for nullity before the element is returned.
E
- the element type for the iteratorcollect
- the collection to clean of nulled referencesnull
references to weak entitiespublic static void runMarkSweep()
null
or garbage reference collection cycle and reclaim
memory lost by those references
This runs the mark sweeping strategy employed by the reference handler. Previous references are swept from
the session and the thread continues to mark the null
references. They are placed into a collection
until the sweeping completes. When this step does complete, the collection is checked, then the references are
purged from the reference queue.
Unlike Java's default GC implementation, this method is strongly bound. This always succeeds in running the collection cycle.
public boolean isNull()
null
or not accessible via the server anymoretrue
to indicate the entity is removed and cannot be further referenced, in which case the
instance of this object should also become null
public T or(T fallback)
isNull()
, then this method will return the
specified fallback object, which must match the type of this object
The provided fallback should not be null
. This is allowed, and is still correct, however, a more
effective strategy is to use entity()
instead.
fallback
- the entity to return in place of the reference in the case that the reference is null
null
, or the fallback if it ispublic T entity()
null
if it has been dereferencednull
if the entity no longer exists on the serverpublic T obtain()
This method will throw an IllegalStateException
if the referenced entity is null
.
Usually, this should be used for debugging purposes, but can be used as a marker in the case that something
within the code goes wrong.
null
public void clear()
If the referenced entity is null
already, no changes will take effect. The call of this method also
has happens-before consistency to any subsequent inspection calls to this WeakEntity.
public Object finder()
searchFor(net.tridentsdk.entity.Entity)
. See that method for the documentation and
implementation.
This method still internally polls the reference queue for the entity's cached finder.
Copyright © 2016. All rights reserved.