Well, I followed this and come with following implementation. @pspeed will appreciate if you comment on this.
Note, I am using Buff component instead of Parent component.
First my (actually it’s yours ) Buff component :
/**
* Associated with entities that apply some effect to another entity.
*
* @author Paul Speed
*/
public class Buff implements PersistentComponent {
private EntityId target;
private int type;
private long startTime;
private int state;
public Buff() {
}
public Buff(EntityId target, int type) {
this(target, type, 0);
}
public Buff(EntityId target, int type, long startTime) {
this(target, type, startTime, 0);
}
public Buff(EntityId target, int type, int state) {
this(target, type, 0, state);
}
public Buff(EntityId target, int type, long startTime, int state) {
this.validateType(type);
this.target = target;
this.type = type;
this.startTime = startTime;
this.state = state;
}
public EntityId getTarget() {
return target;
}
public int getType() {
return type;
}
public long getStartTime() {
return startTime;
}
public int getState() {
return state;
}
public Buff adjustTarget(EntityId target){
return new Buff(target, type, startTime, state);
}
public Buff adjustState(int state) {
return new Buff(target, type, startTime, state);
}
@Override
public String toString() {
return "Buff[" + target + ", at:" + startTime + "]";
}
private void validateType(int type) {
if (type == 0) {
throw new RuntimeException("Unknow buff type!");
}
}
}
and BuffVisibility :
/**
* Limits the client's visibility of any entity containing a Buff
* to whatever is in the current scene client is attached to.
*
* @author Ali-RS
*/
public class BuffVisibility implements ComponentVisibility {
static Logger log = LoggerFactory.getLogger(BuffVisibility.class);
private EntityData ed;
private BufferedHashSet<EntityId> currentSceneBuffs;
private BufferedHashSet<EntityId> lastSceneBuffs;
/**
*
* @param sceneBuffs containing all the buffed entities in current scene that client is attached.
*/
public BuffVisibility(BufferedHashSet<EntityId> sceneBuffs) {
this.currentSceneBuffs = sceneBuffs;
}
/**
* Set new scene buffs if scene has changed.
*
* @param sceneBuffs
*/
public void setCurrentSceneBuffs(BufferedHashSet<EntityId> sceneBuffs) {
this.lastSceneBuffs = this.currentSceneBuffs;
this.currentSceneBuffs = sceneBuffs;
}
@Override
public Class<? extends EntityComponent> getComponentType() {
return Buff.class;
}
@Override
public void initialize(EntityData ed) {
this.ed = ed;
}
@Override
public <T extends EntityComponent> T getComponent(EntityId entityId, Class<T> type) {
log.info("getComponent(" + entityId + ", " + type + ")");
if (!currentSceneBuffs.contains(entityId)) {
return null;
}
return ed.getComponent(entityId, type);
}
@Override
public Set<EntityId> getEntityIds(ComponentFilter filter) {
if (log.isTraceEnabled()) {
log.trace("getEntityIds(" + filter + ")");
}
if (filter != null) {
throw new UnsupportedOperationException("Filtering + buff visibility not yet supported");
}
return currentSceneBuffs.getSnapshot();
}
public boolean collectChanges(Queue<EntityChange> updates) {
boolean changed = false;
// if( log.isTraceEnabled() ) {
// log.trace("active:" + active);
// log.info("updates before:" + updates);
// }
// Remove buffs from previous scene
if(lastSceneBuffs != null){
updates.addAll(Collections2.transform(lastSceneBuffs.getSnapshot(), id -> new EntityChange(id, Buff.class)));
lastSceneBuffs = null;
}
// Remove any Buff updates that don't belong to the client scene
for (Iterator<EntityChange> it = updates.iterator(); it.hasNext();) {
EntityChange change = it.next();
if (change.getComponentType() == Buff.class
&& !currentSceneBuffs.contains(change.getEntityId())) {
if (log.isTraceEnabled()) {
log.trace("removing irrelevant change:" + change);
}
it.remove();
}
}
return changed;
}
}
and finally BuffSystem :
/**
* Keep track of the buff IDs for each scene.
*
* @author Ali-RS
*/
public class BuffSystem extends AbstractGameSystem {
static Logger log = LoggerFactory.getLogger(BuffSystem.class);
private EntityData ed;
private EntitySet buffSet;
// Keep track of buffs by sceneId
private final Map<EntityId, BufferedHashSet<EntityId>> buffIndex = new HashMap();
// Keep track of sceneId by buffId
private final Map<EntityId, EntityId> sceneIndex = new HashMap<>();
private final Queue<EntityId> pendingCommit = new ArrayDeque<>();
public BufferedHashSet<EntityId> getBuffs(EntityId sceneId) {
synchronized (buffIndex) {
return buffIndex.get(sceneId);
}
}
@Override
protected void initialize() {
ed = getSystem(EntityData.class, true);
buffSet = ed.getEntities(Buff.class);
}
@Override
protected void terminate() {
buffSet.release();
buffIndex.clear();
}
@Override
public void update(SimTime time) {
if (buffSet.applyChanges()) {
for (Entity entity : buffSet.getAddedEntities()) {
addBuff(entity);
}
for (Entity entity : buffSet.getRemovedEntities()) {
removeBuff(entity);
}
}
applyPendingCommit();
}
private void addBuff(Entity entity) {
Buff buff = entity.get(Buff.class);
switch (buff.getType()) {
case BuffTypes.BODY:
EntityId sceneId = buff.getTarget();
getBuffSet(sceneId).add(entity.getId());
sceneIndex.put(entity.getId(), sceneId);
addToCommit(sceneId);
break;
case BuffTypes.MOBILITY:
case BuffTypes.ACTION:
EntityId bodyId = buff.getTarget();
sceneId = buffSet.getEntity(bodyId).get(Buff.class).getTarget();
getBuffSet(sceneId).add(entity.getId());
sceneIndex.put(entity.getId(), sceneId);
addToCommit(sceneId);
break;
}
}
private void removeBuff(Entity entity) {
EntityId sceneId = sceneIndex.remove(entity.getId());
buffIndex.get(sceneId).remove(entity.getId());
addToCommit(sceneId);
}
private BufferedHashSet<EntityId> getBuffSet(EntityId sceneId) {
if (!buffIndex.containsKey(sceneId)) {
buffIndex.put(sceneId, new BufferedHashSet<>());
}
return buffIndex.get(sceneId);
}
private void addToCommit(EntityId sceneId) {
if (!pendingCommit.contains(sceneId)) {
pendingCommit.add(sceneId);
}
}
private void applyPendingCommit() {
while (!pendingCommit.isEmpty()) {
EntityId sceneId = pendingCommit.poll();
buffIndex.get(sceneId).commit();
}
}
}