Skip to content

Commit

Permalink
Fix noded topology cache invalidation
Browse files Browse the repository at this point in the history
The cache is moved to `ZoneView` to better match the 1.15 and earlier cache. The cache is now cleared on `TokensChanged`
events as well as `TopologyChanged`, as it was before.
  • Loading branch information
kwvanderlinde committed Jan 24, 2025
1 parent b11ff77 commit 4f8dadc
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 33 deletions.
27 changes: 23 additions & 4 deletions src/main/java/net/rptools/maptool/client/ui/zone/ZoneView.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import net.rptools.maptool.client.ui.zone.IlluminationModel.ContributedLight;
import net.rptools.maptool.client.ui.zone.IlluminationModel.LightInfo;
import net.rptools.maptool.client.ui.zone.Illuminator.LitArea;
import net.rptools.maptool.client.ui.zone.vbl.NodedTopology;
import net.rptools.maptool.events.MapToolEventBus;
import net.rptools.maptool.model.*;
import net.rptools.maptool.model.player.Player;
Expand Down Expand Up @@ -150,6 +151,9 @@ private void addLightSourceToken(Token token, Set<Player.Role> roles) {
/** Holds the auras from lightSourceMap after they have been combined. */
private final Map<PlayerView, List<DrawableLight>> drawableAuras = new HashMap<>();

/** Cached version of the zone's topology which is fully noded. */
private NodedTopology nodedTopology = null;

/**
* Construct ZoneView from zone. Build lightSourceMap, and add ZoneView to Zone as listener.
*
Expand Down Expand Up @@ -226,6 +230,19 @@ public boolean isUsingVision() {
return zone.getVisionType() != Zone.VisionType.OFF;
}

/**
* Packages mask topology, including token masks, together with wall topology, adding nodes at any
* intersection points.
*/
private synchronized NodedTopology prepareNodedTopology() {
if (nodedTopology == null) {
var walls = zone.getWalls();
var masks = zone.getMasks(EnumSet.allOf(Zone.TopologyType.class), null);
nodedTopology = NodedTopology.prepare(walls, masks);
}
return nodedTopology;
}

private IlluminationModel getIlluminationModel(IlluminationKey illuminationKey) {
final var illuminationModel =
illuminationModels.computeIfAbsent(illuminationKey, key -> new IlluminationModel());
Expand Down Expand Up @@ -287,7 +304,7 @@ private List<ContributedLight> calculateLitAreaForLightSource(

if (!lightSource.isIgnoresVBL()) {
lightSourceVisibleArea =
FogUtil.calculateVisibility(p, lightSourceArea, zone.prepareNodedTopologies());
FogUtil.calculateVisibility(p, lightSourceArea, prepareNodedTopology());
}
if (lightSourceVisibleArea.isEmpty()) {
// Nothing illuminated for this source.
Expand Down Expand Up @@ -526,7 +543,7 @@ private Area getTokenVisibleArea(@Nonnull Token token) {
Point p = FogUtil.calculateVisionCenter(token, zone);
Area visibleArea = sight.getVisionShape(token, zone);
visibleArea.transform(AffineTransform.getTranslateInstance(p.x, p.y));
tokenVisibleArea = FogUtil.calculateVisibility(p, visibleArea, zone.prepareNodedTopologies());
tokenVisibleArea = FogUtil.calculateVisibility(p, visibleArea, prepareNodedTopology());
tokenVisibleAreaCache.put(token.getId(), tokenVisibleArea);
}

Expand Down Expand Up @@ -612,8 +629,7 @@ public List<DrawableLight> getDrawableAuras(PlayerView view) {

if (!lightSource.isIgnoresVBL()) {
visibleArea =
FogUtil.calculateVisibility(
p, lightSourceArea, zone.prepareNodedTopologies());
FogUtil.calculateVisibility(p, lightSourceArea, prepareNodedTopology());
}

// This needs to be cached somehow
Expand Down Expand Up @@ -788,6 +804,7 @@ public void flush(Token token) {

private void onTopologyChanged() {
flush();
nodedTopology = null;
}

@Subscribe
Expand Down Expand Up @@ -856,6 +873,7 @@ private void onTokensRemoved(TokensRemoved event) {

if (event.tokens().stream().anyMatch(Token::hasAnyMaskTopology)) {
flush();
nodedTopology = null;
}
}

Expand Down Expand Up @@ -885,6 +903,7 @@ private void processTokenAddChangeEvent(List<Token> tokens) {

if (tokens.stream().anyMatch(Token::hasAnyMaskTopology)) {
flush();
nodedTopology = null;
}
}

Expand Down
29 changes: 0 additions & 29 deletions src/main/java/net/rptools/maptool/model/Zone.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import net.rptools.maptool.client.ui.zone.PlayerView;
import net.rptools.maptool.client.ui.zone.ZoneView;
import net.rptools.maptool.client.ui.zone.renderer.ZoneRenderer;
import net.rptools.maptool.client.ui.zone.vbl.NodedTopology;
import net.rptools.maptool.events.MapToolEventBus;
import net.rptools.maptool.language.I18N;
import net.rptools.maptool.model.InitiativeList.TokenInitiative;
Expand Down Expand Up @@ -391,8 +390,6 @@ public enum TopologyType {
// The new take on topology.
private WallTopology walls = new WallTopology();

private transient @Nullable NodedTopology nodedTopology;

// The 'board' layer, at the very bottom of the layer stack.
// Itself has two sub-layers:
// The top one is an optional texture, typically a pre-drawn map.
Expand Down Expand Up @@ -992,25 +989,9 @@ public List<MaskTopology> getMasks(Set<TopologyType> types, @Nullable GUID exclu

public void replaceWalls(WallTopology walls) {
this.walls = walls;
this.nodedTopology = null;
new MapToolEventBus().getMainEventBus().post(new WallTopologyChanged(this));
}

/**
* Packages legacy topology, including token topology, together with wall topology, adding nodes
* at any intersection points.
*
* @return
*/
public NodedTopology prepareNodedTopologies() {
if (nodedTopology == null) {
var legacyMasks = getMasks(EnumSet.allOf(TopologyType.class), null);
nodedTopology = NodedTopology.prepare(walls, legacyMasks);
}

return nodedTopology;
}

public Area getMaskTopology(TopologyType topologyType) {
return switch (topologyType) {
case WALL_VBL -> topology;
Expand Down Expand Up @@ -1043,21 +1024,11 @@ public void updateMaskTopology(Area area, boolean erase, TopologyType topologyTy
topology.add(area);
}

// MBL doesn't affect vision, so no need to invalidate the noding.
if (topologyType != TopologyType.MBL) {
nodedTopology = null;
}
new MapToolEventBus().getMainEventBus().post(new MaskTopologyChanged(this));
}

/** Fire the event {@link MaskTopologyChanged}. */
public void tokenMaskTopologyChanged(Collection<TopologyType> types) {
if (types.contains(TopologyType.WALL_VBL)
|| types.contains(TopologyType.HILL_VBL)
|| types.contains(TopologyType.PIT_VBL)
|| types.contains(TopologyType.COVER_VBL)) {
nodedTopology = null;
}
new MapToolEventBus().getMainEventBus().post(new MaskTopologyChanged(this));
}

Expand Down

0 comments on commit 4f8dadc

Please sign in to comment.