maptool
maptool copied to clipboard
[Bug]: Concurrency issue with rendering lights
Describe the Bug
When constantly re-rendering a map (e.g., by using macros to push around a token with an equipped light source), it is possible for the cache of drawable light sources to be flushed (set to null
) while we are still trying to render the lights, resulting in a NullPointerException
.
To Reproduce
- Open this campaign (rename to
npe-lights.cmpgn
first): npe-lights.zip - Select the "Hero" token, and click the "Animate Me" macro.
- Wait for a while, and when you're unlucky...
- See the dialog pop up with the message
java.lang.NullPointerException: Cannot invoke "java.util.List.stream()" because "this.drawableLights" is null
Expected Behaviour
Lights can be rendered without generating user-visible errors.
Screenshots
No response
MapTool Info
develop
Desktop
Linux Mint 20.3
Additional Context
java.lang.NullPointerException: Cannot invoke "java.util.List.stream()" because "this.drawableLights" is null at net.rptools.maptool.client.ui.zone.ZoneRenderer.renderLights(ZoneRenderer.java:1447) at net.rptools.maptool.client.ui.zone.ZoneRenderer.renderZone(ZoneRenderer.java:1224) at net.rptools.maptool.client.ui.zone.ZoneRenderer.paintComponent(ZoneRenderer.java:846) at java.desktop/javax.swing.JComponent.paint(JComponent.java:1119) at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:952) at java.desktop/javax.swing.JComponent.paint(JComponent.java:1128) at java.desktop/javax.swing.JComponent.paintToOffscreen(JComponent.java:5311) at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBufferedImpl(RepaintManager.java:1657) at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1632) at java.desktop/javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1570) at java.desktop/javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:262) at java.desktop/javax.swing.RepaintManager.paint(RepaintManager.java:1337) at java.desktop/javax.swing.JComponent._paintImmediately(JComponent.java:5259) at java.desktop/javax.swing.JComponent.paintImmediately(JComponent.java:5069) at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:879) at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:862) at java.base/java.security.AccessController.doPrivileged(AccessController.java:399) at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86) at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:862) at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:835) at java.desktop/javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:784) at java.desktop/javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1898) at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318) at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:771) at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:722) at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:716) at java.base/java.security.AccessController.doPrivileged(AccessController.java:399) at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86) at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:741) at net.rptools.maptool.client.swing.MapToolEventQueue.dispatchEvent(MapToolEventQueue.java:54) at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:117) at java.desktop/java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:191) at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:236) at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:234) at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) at java.desktop/java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:234) at java.desktop/java.awt.Dialog.show(Dialog.java:1080) at java.desktop/java.awt.Component.show(Component.java:1728) at java.desktop/java.awt.Component.setVisible(Component.java:1675) at java.desktop/java.awt.Window.setVisible(Window.java:1036) at java.desktop/java.awt.Dialog.setVisible(Dialog.java:1016) at net.rptools.maptool.client.swing.MapToolEventQueue.displayPopup(MapToolEventQueue.java:109) at net.rptools.maptool.client.swing.MapToolEventQueue.dispatchEvent(MapToolEventQueue.java:73) at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:117) at java.desktop/java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:191) at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:236) at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:234) at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) at java.desktop/java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:234) at java.desktop/java.awt.Dialog.show(Dialog.java:1080) at java.desktop/java.awt.Component.show(Component.java:1728) at java.desktop/java.awt.Component.setVisible(Component.java:1675) at java.desktop/java.awt.Window.setVisible(Window.java:1036) at java.desktop/java.awt.Dialog.setVisible(Dialog.java:1016) at net.rptools.maptool.client.swing.MapToolEventQueue.displayPopup(MapToolEventQueue.java:109) at net.rptools.maptool.client.swing.MapToolEventQueue.dispatchEvent(MapToolEventQueue.java:73) at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:117) at java.desktop/java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:191) at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:236) at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:234) at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) at java.desktop/java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:234) at java.desktop/java.awt.Dialog.show(Dialog.java:1080) at java.desktop/java.awt.Component.show(Component.java:1728) at java.desktop/java.awt.Component.setVisible(Component.java:1675) at java.desktop/java.awt.Window.setVisible(Window.java:1036) at java.desktop/java.awt.Dialog.setVisible(Dialog.java:1016) at net.rptools.maptool.client.swing.MapToolEventQueue.displayPopup(MapToolEventQueue.java:109) at net.rptools.maptool.client.swing.MapToolEventQueue.dispatchEvent(MapToolEventQueue.java:73) at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:117) at java.desktop/java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:191) at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:236) at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:234) at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) at java.desktop/java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:234) at java.desktop/java.awt.Dialog.show(Dialog.java:1080) at java.desktop/java.awt.Component.show(Component.java:1728) at java.desktop/java.awt.Component.setVisible(Component.java:1675) at java.desktop/java.awt.Window.setVisible(Window.java:1036) at java.desktop/java.awt.Dialog.setVisible(Dialog.java:1016) at net.rptools.maptool.client.swing.MapToolEventQueue.displayPopup(MapToolEventQueue.java:109) at net.rptools.maptool.client.swing.MapToolEventQueue.dispatchEvent(MapToolEventQueue.java:73) at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:117) at java.desktop/java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:191) at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:236) at java.desktop/java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:234) at java.base/java.security.AccessController.doPrivileged(AccessController.java:318) at java.desktop/java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:234) at java.desktop/java.awt.Dialog.show(Dialog.java:1080) at java.desktop/java.awt.Component.show(Component.java:1728) at java.desktop/java.awt.Component.setVisible(Component.java:1675) at java.desktop/java.awt.Window.setVisible(Window.java:1036) at java.desktop/java.awt.Dialog.setVisible(Dialog.java:1016) at net.rptools.maptool.client.swing.MapToolEventQueue.displayPopup(MapToolEventQueue.java:109) at net.rptools.maptool.client.swing.MapToolEventQueue.dispatchEvent(MapToolEventQueue.java:73) at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113) at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109) at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
While I was working on reproducing the issue, I encountered another similar issue with ZoneView
's light source cache
java.util.ConcurrentModificationException at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1221) at net.rptools.maptool.client.ui.zone.ZoneView.addLightSourceToCache(ZoneView.java:324) at net.rptools.maptool.client.ui.zone.ZoneView.calculateLightSourceArea(ZoneView.java:268) at net.rptools.maptool.client.ui.zone.ZoneView.calculateLightSourceArea(ZoneView.java:226) at net.rptools.maptool.client.ui.zone.ZoneView.getLightSourceArea(ZoneView.java:181) at net.rptools.maptool.client.ui.zone.ZoneView.getLightAreasByLumens(ZoneView.java:485) at net.rptools.maptool.client.ui.zone.ZoneView.getVisibleArea(ZoneView.java:425) at net.rptools.maptool.client.ui.zone.ZoneView.calculateVisibleArea(ZoneView.java:684) at net.rptools.maptool.client.ui.zone.ZoneView.getVisibleArea(ZoneView.java:80) at net.rptools.maptool.client.ui.zone.ZoneRenderer.renderZone(ZoneRenderer.java:1142) at net.rptools.maptool.client.ui.zone.ZoneRenderer.paintComponent(ZoneRenderer.java:846) at java.desktop/javax.swing.JComponent.paint(JComponent.java:1119) at java.desktop/javax.swing.JComponent.paintChildren(JComponent.java:952) at java.desktop/javax.swing.JComponent.paint(JComponent.java:1128) at java.desktop/javax.swing.JComponent.paintToOffscreen(JComponent.java:5311) at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBufferedImpl(RepaintManager.java:1657) at java.desktop/javax.swing.RepaintManager$PaintManager.paintDoubleBuffered(RepaintManager.java:1632) at java.desktop/javax.swing.RepaintManager$PaintManager.paint(RepaintManager.java:1570) at java.desktop/javax.swing.BufferStrategyPaintManager.paint(BufferStrategyPaintManager.java:262) at java.desktop/javax.swing.RepaintManager.paint(RepaintManager.java:1337) at java.desktop/javax.swing.JComponent._paintImmediately(JComponent.java:5259) at java.desktop/javax.swing.JComponent.paintImmediately(JComponent.java:5069) at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:879) at java.desktop/javax.swing.RepaintManager$4.run(RepaintManager.java:862) at java.base/java.security.AccessController.doPrivileged(AccessController.java:399) at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86) at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:862) at java.desktop/javax.swing.RepaintManager.paintDirtyRegions(RepaintManager.java:835) at java.desktop/javax.swing.RepaintManager.prePaintDirtyRegions(RepaintManager.java:784) at java.desktop/javax.swing.RepaintManager$ProcessingRunnable.run(RepaintManager.java:1898) at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318) at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:771) at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:722) at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:716) at java.base/java.security.AccessController.doPrivileged(AccessController.java:399) at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86) at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:741) at net.rptools.maptool.client.swing.MapToolEventQueue.dispatchEvent(MapToolEventQueue.java:54) at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124) at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113) at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109) at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
There is also some noticeable flickering during the animation (i.e., sometimes the lights look like they just aren't rendering). I'm not certain that it's related, but it's mighty suspicious.
I am not able to reproduce any of the problems (errors or flickering) on 1.11.5.
I'm unable to reproduce this on Windows (10) with the dev branch. I did see a few flickers, but they were very infrequent.
Closing this as I can no longer reproduce it (errors or flickering) since 1.13.