Zay-ES and hidden states
The reason I asked is because there is already a facility for filtering out entities by a particular component. That’s how Mythruna works. If the entity has a BodyPosition outside of the client’s visible zones, that client can’t see anything about it.
This uses the ComponentVisibility feature. I guess my bpos stuff is still in the unreleased MOSS libraries so I’ll paste the example here.
/*
* $Id$
*
* Copyright (c) 2021, Simsilica, LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.simsilica.bpos.net;
import java.util.*;
import org.slf4j.*;
import com.simsilica.es.*;
import com.simsilica.es.server.ComponentVisibility;
import com.simsilica.ethereal.NetworkStateListener;
import com.simsilica.bpos.BodyPosition;
/**
* Used on the server for a specific client connection to limit that client's
* visibility of any entity containing a BodyPosition to just what the
* SimEthereal visibility says they can see. This is usually added by a
* server-side service during client initialization.
*
* @author Paul Speed
*/
public class BodyVisibility implements ComponentVisibility {
static Logger log = LoggerFactory.getLogger(BodyVisibility.class);
private NetworkStateListener netState;
private EntityData ed;
private Set<Long> lastActiveIds;
private Map<EntityId, BodyPosition> lastValues = new HashMap<>();
protected BodyVisibility( NetworkStateListener netState, Set<Long> lastActiveIds ) {
this.netState = netState;
this.lastActiveIds = lastActiveIds;
}
public BodyVisibility( NetworkStateListener netState ) {
this(netState, null);
}
@Override
public Class<? extends EntityComponent> getComponentType() {
return BodyPosition.class;
}
@Override
public void initialize( EntityData ed ) {
this.ed = ed;
}
@Override
public <T extends EntityComponent> T getComponent( EntityId entityId, Class<T> type ) {
if( log.isTraceEnabled() ) {
log.trace("getComponent(" + entityId + ", " + type + ")");
}
//if( !netState.getActiveIds().contains(entityId) ) {
// return null;
//}
if( !lastValues.containsKey(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 + body visibility not yet supported");
}
/*Set<Long> active = netState.getActiveIds();
log.info("active:" + active);
Set<EntityId> results = new HashSet<>();
for( Long l : active ) {
results.add(new EntityId(l));
}
return results;*/
return lastValues.keySet();
}
public boolean collectChanges( Queue<EntityChange> updates ) {
Set<Long> active = netState.getActiveIds();
boolean changed = false;
if( log.isTraceEnabled() ) {
log.trace("active:" + active);
log.trace("updates before:" + updates);
}
// Remove any BodyPosition updates that don't belong to the active
// set
for( Iterator<EntityChange> it = updates.iterator(); it.hasNext(); ) {
EntityChange change = it.next();
if( change.getComponentType() == BodyPosition.class
&& !active.contains(change.getEntityId().getId()) ) {
if( log.isTraceEnabled() ) {
log.trace("removing irrelevant change:" + change);
}
it.remove();
}
}
// First process the removals
for( Iterator<EntityId> it = lastValues.keySet().iterator(); it.hasNext(); ) {
EntityId id = it.next();
if( active.contains(id.getId()) ) {
continue;
}
if( log.isTraceEnabled() ) {
log.trace("removing:" + id);
}
updates.add(new EntityChange(id, BodyPosition.class));
it.remove();
changed = true;
}
// Now the adds
for( Long l : active ) {
EntityId id = new EntityId(l);
if( lastValues.containsKey(id) ) {
continue;
}
if( log.isTraceEnabled() ) {
log.trace("adding:" + id);
}
BodyPosition pos = ed.getComponent(id, BodyPosition.class);
lastValues.put(id, pos);
updates.add(new EntityChange(id, pos));
changed = true;
}
if( changed ) {
if( log.isTraceEnabled() ) {
log.trace("done collectChanges() " + active);
}
}
return changed;
}
}
On the server you just register a ComponentVisibility object with the client-specific hosted entity data.
Edit: rereading the classes, I guess it doesn’t prevent all queries against the entity but will filter the entity out of entity sets that have that component. I’d have to experiment to be 100% sure because my memory says one thing and the javadoc says another.
Discussion in the ATmosphere