jalse.entities.DefaultEntityFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of JALSE Show documentation
Show all versions of JALSE Show documentation
Java Artificial Life Simulation Engine
package jalse.entities;
import static jalse.actions.Actions.requireNotStopped;
import jalse.actions.ActionEngine;
import jalse.actions.Actions;
import jalse.actions.ForkJoinActionEngine;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Logger;
/**
* A {@link EntityFactory} implementation that creates/kills {@link DefaultEntity}. Default entity
* factory can have a total entity limit set. When this factory kills an entity it will kill the
* entity tree under it (can only kill entities his factory has created).
*
* This factory assumes all source containers (and when importing target containers) are genuine.
*
* If no {@link ActionEngine} is supplied {@link ForkJoinActionEngine#commonPoolEngine()} will be
* used.
*
* @author Elliot Ford
*
*/
public class DefaultEntityFactory implements EntityFactory {
private static final Logger logger = Logger.getLogger(DefaultEntityFactory.class.getName());
private final int entityLimit;
private final Set entityIDs;
private ActionEngine engine;
private int entityCount;
private final Lock read;
private final Lock write;
/**
* Creates a default entity factory with no entity limit.
*/
public DefaultEntityFactory() {
this(Integer.MAX_VALUE);
}
/**
* Creates a default entity factory with the supplied entity limit.
*
* @param entityLimit
* Maximum entity limit.
*/
public DefaultEntityFactory(final int entityLimit) {
if (entityLimit <= 0) {
throw new IllegalArgumentException();
}
this.entityLimit = entityLimit;
entityIDs = new HashSet<>();
engine = ForkJoinActionEngine.commonPoolEngine(); // Defaults use common engine
entityCount = 0;
final ReadWriteLock rwLock = new ReentrantReadWriteLock();
read = rwLock.readLock();
write = rwLock.writeLock();
}
@Override
public void exportEntity(final Entity e) {
final UUID eID = e.getID();
write.lock();
try {
if (!entityIDs.remove(eID)) {
throw new IllegalArgumentException(String.format("Does not know of entity %s", eID));
}
} finally {
write.unlock();
}
final ActionEngine emptyEngine = Actions.emptyActionEngine();
final DefaultEntity de = (DefaultEntity) e;
de.cancelAllScheduledForActor();
de.setEngine(emptyEngine);
de.setContainer(null); // Remove parent reference.
Entities.walkEntities(e).map(DefaultEntity.class::cast).forEach(ce -> {
ce.cancelAllScheduledForActor();
ce.setEngine(emptyEngine);
});
logger.fine(String.format("Entity %s exported", eID));
}
/**
* Gets the associated engine.
*
* @return Action engine.
*/
public ActionEngine getEngine() {
read.lock();
try {
return engine;
} finally {
read.unlock();
}
}
/**
* Gets the current total entity count.
*
* @return Entity count.
*/
public int getEntityCount() {
read.lock();
try {
return entityCount;
} finally {
read.unlock();
}
}
/**
* Gets the total entity limit.
*
* @return Entity limit.
*/
public int getEntityLimit() {
return entityLimit;
}
@Override
public DefaultEntity newEntity(final UUID id, final EntityContainer target) {
Objects.requireNonNull(id);
Objects.requireNonNull(target);
write.lock();
try {
if (entityCount >= entityLimit) {
throw new IllegalStateException(String.format("Entity limit of %d has been reached", entityLimit));
}
// Unique only
if (!entityIDs.add(id)) {
throw new IllegalArgumentException(String.format("Entity %s is already associated", id));
}
final DefaultEntity e = new DefaultEntity(id, this, target);
e.setEngine(engine);
e.markAsAlive();
entityCount++;
logger.fine(String.format("Entity %s created", id));
return e;
} finally {
write.unlock();
}
}
@Override
public void setEngine(final ActionEngine engine) {
Objects.requireNonNull(engine);
write.lock();
try {
logger.fine(String.format("Switching engine type %s to %s", this.engine.getClass(), engine.getClass()));
this.engine = requireNotStopped(engine);
} finally {
write.unlock();
}
}
@Override
public boolean tryImportEntity(final Entity e, final EntityContainer target) {
final UUID eID = e.getID();
write.lock();
try {
if (!entityIDs.add(eID)) {
return false;
}
final DefaultEntity de = (DefaultEntity) e;
de.setEngine(engine);
de.setContainer(target);
Entities.walkEntities(de).map(DefaultEntity.class::cast).forEach(ve -> ve.setEngine(engine));
logger.fine(String.format("Entity %s imported", eID));
return true;
} finally {
write.unlock();
}
}
@Override
public boolean tryKillEntity(final Entity e) {
final UUID eID = e.getID();
write.lock();
try {
final DefaultEntity de = (DefaultEntity) e;
if (!entityIDs.remove(eID) || !de.isAlive()) { // Kill only those in need
return false;
}
de.markAsDead();
de.cancelAllScheduledForActor();
de.setEngine(null);
entityCount--;
de.killEntities(); // Kill tree
logger.fine(String.format("Entity %s killed", eID));
return true;
} finally {
write.unlock();
}
}
@Override
public boolean tryTakeFromTree(final Entity e, final EntityContainer target) {
final UUID eID = e.getID();
write.lock();
try {
if (!entityIDs.contains(eID)) {
return false;
}
((DefaultEntity) e).setContainer(target);
logger.fine(String.format("Entity %s taken from tree", eID));
return true;
} finally {
write.unlock();
}
}
@Override
public boolean withinSameTree(final EntityContainer source, final EntityContainer target) {
Objects.requireNonNull(source);
Objects.requireNonNull(target);
read.lock();
try {
return Entities.withinSameTree(source, target);
} finally {
read.unlock();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy