Skip to content

Commit 14c879c

Browse files
committed
perf(render): bypass redundant viewer lookups
1 parent 774d0a9 commit 14c879c

2 files changed

Lines changed: 65 additions & 22 deletions

File tree

src/main/java/top/ellan/mahjong/table/render/SparrowRayInteractionProxyCoordinator.java

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -78,23 +78,23 @@ synchronized void replace(
7878
if (viewerId == null || interactions == null || interactions.isEmpty()) {
7979
continue;
8080
}
81-
Player viewer = this.session.onlinePlayer(viewerId);
82-
if (viewer == null || !viewer.isOnline()) {
83-
continue;
84-
}
8581
ActiveProxies current = previous.get(viewerId);
86-
if (this.canReuse(viewerId, viewer, current, interactions)) {
82+
if (current != null
83+
&& current.viewer().isOnline()
84+
&& this.canReuse(viewerId, current, interactions)) {
8785
next.put(viewerId, current);
8886
continue;
8987
}
90-
List<DisplayInteractionRayRegistry.RayInteraction> stableInteractions = List.copyOf(interactions);
91-
List<InteractionGeometry> geometry = interactionGeometry(stableInteractions);
92-
List<ClientProxy> proxies = List.copyOf(this.backend.create(viewer, stableInteractions));
88+
Player viewer = this.session.onlinePlayer(viewerId);
89+
if (viewer == null || !viewer.isOnline()) {
90+
continue;
91+
}
92+
List<InteractionGeometry> geometry = interactionGeometry(interactions);
93+
List<ClientProxy> proxies = List.copyOf(this.backend.create(viewer, interactions));
9394
if (!proxies.isEmpty()) {
9495
ActiveProxies created = new ActiveProxies(
9596
viewer,
9697
proxies,
97-
stableInteractions,
9898
geometry,
9999
proxies.get(0).entityId()
100100
);
@@ -156,12 +156,10 @@ private boolean canReuseRegion(
156156
if (viewerId == null || interactions == null || interactions.isEmpty()) {
157157
continue;
158158
}
159-
Player viewer = this.session.onlinePlayer(viewerId);
160-
if (viewer == null || !viewer.isOnline()) {
161-
continue;
162-
}
163159
ActiveProxies active = previous.get(viewerId);
164-
if (!this.canReuse(viewerId, viewer, active, interactions)) {
160+
if (active == null
161+
|| !active.viewer().isOnline()
162+
|| !this.canReuse(viewerId, active, interactions)) {
165163
return false;
166164
}
167165
reusableViewers++;
@@ -171,14 +169,10 @@ private boolean canReuseRegion(
171169

172170
private boolean canReuse(
173171
UUID viewerId,
174-
Player viewer,
175172
ActiveProxies active,
176173
List<DisplayInteractionRayRegistry.RayInteraction> interactions
177174
) {
178-
if (active == null || active.viewer() != viewer || active.proxies().isEmpty()) {
179-
return false;
180-
}
181-
if (active.interactions() != interactions && !sameGeometry(active.geometry(), interactions)) {
175+
if (active == null || active.proxies().isEmpty() || !sameGeometry(active.geometry(), interactions)) {
182176
return false;
183177
}
184178
return this.hasOwnership(viewerId, active);
@@ -391,7 +385,6 @@ interface ClientProxy {
391385
private record ActiveProxies(
392386
Player viewer,
393387
List<ClientProxy> proxies,
394-
List<DisplayInteractionRayRegistry.RayInteraction> interactions,
395388
List<InteractionGeometry> geometry,
396389
int firstEntityId
397390
) {

src/test/java/top/ellan/mahjong/table/render/SparrowRayInteractionProxyCoordinatorTest.java

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ void clearedViewerOwnershipMakesAnUnchangedRegionStaleForReconnect() {
126126
}
127127

128128
@Test
129-
void sameImmutableInteractionSnapshotReusesWithoutRepeatedIdentityReads() {
129+
void unchangedRegionReusesWithoutRepeatedSessionOrIdentityReads() {
130130
TableSessionContext session = mock(TableSessionContext.class);
131131
SparrowRayInteractionProxyCoordinator.Backend backend = mock(
132132
SparrowRayInteractionProxyCoordinator.Backend.class
@@ -162,11 +162,12 @@ void sameImmutableInteractionSnapshotReusesWithoutRepeatedIdentityReads() {
162162
verify(backend, times(1)).spawn(viewer, List.of(proxy));
163163
verify(proxy, times(1)).entityId();
164164
verify(session, times(1)).id();
165+
verify(session, times(1)).onlinePlayer(VIEWER_ID);
165166
assertEquals(1, coordinator.entityCount());
166167
}
167168

168169
@Test
169-
void mutableInteractionListGeometryChangeCannotHitIdentityFastPath() {
170+
void mutableInteractionListGeometryChangeForcesRebuild() {
170171
TableSessionContext session = mock(TableSessionContext.class);
171172
SparrowRayInteractionProxyCoordinator.Backend backend = mock(
172173
SparrowRayInteractionProxyCoordinator.Backend.class
@@ -256,6 +257,55 @@ void multipleProxyOwnershipRemainsCompleteWithCachedFirstEntityId() {
256257
verify(secondProxy, times(3)).entityId();
257258
}
258259

260+
@Test
261+
void offlineCachedViewerUsesFreshSessionPlayer() {
262+
TableSessionContext session = mock(TableSessionContext.class);
263+
SparrowRayInteractionProxyCoordinator.Backend backend = mock(
264+
SparrowRayInteractionProxyCoordinator.Backend.class
265+
);
266+
SparrowRayInteractionProxyCoordinator.ClientProxy firstProxy = mock(
267+
SparrowRayInteractionProxyCoordinator.ClientProxy.class
268+
);
269+
SparrowRayInteractionProxyCoordinator.ClientProxy reconnectedProxy = mock(
270+
SparrowRayInteractionProxyCoordinator.ClientProxy.class
271+
);
272+
Player firstViewer = mock(Player.class);
273+
Player reconnectedViewer = mock(Player.class);
274+
when(session.id()).thenReturn("table-a");
275+
when(session.onlinePlayer(VIEWER_ID)).thenReturn(firstViewer).thenReturn(reconnectedViewer);
276+
when(firstViewer.isOnline()).thenReturn(true);
277+
when(reconnectedViewer.isOnline()).thenReturn(true);
278+
when(backend.available()).thenReturn(true);
279+
when(backend.create(eq(firstViewer), any())).thenReturn(List.of(firstProxy));
280+
when(backend.create(eq(reconnectedViewer), any())).thenReturn(List.of(reconnectedProxy));
281+
when(firstProxy.entityId()).thenReturn(1211);
282+
when(reconnectedProxy.entityId()).thenReturn(1212);
283+
doAnswer(invocation -> {
284+
invocation.<Runnable>getArgument(1).run();
285+
return null;
286+
}).when(session).runForViewer(any(Player.class), any(Runnable.class));
287+
SparrowRayInteractionProxyCoordinator coordinator = new SparrowRayInteractionProxyCoordinator(
288+
session,
289+
backend
290+
);
291+
Map<UUID, List<DisplayInteractionRayRegistry.RayInteraction>> interactions = Map.of(
292+
VIEWER_ID,
293+
List.of(interaction())
294+
);
295+
296+
coordinator.replace("actions", interactions);
297+
when(firstViewer.isOnline()).thenReturn(false);
298+
coordinator.replace("actions", interactions);
299+
300+
verify(backend).spawn(firstViewer, List.of(firstProxy));
301+
verify(backend).spawn(reconnectedViewer, List.of(reconnectedProxy));
302+
verify(backend, never()).destroy(firstViewer, List.of(firstProxy));
303+
verify(session, times(2)).onlinePlayer(VIEWER_ID);
304+
assertNull(ClientInteractionProxyRegistry.tableIdFor(1211, VIEWER_ID));
305+
assertEquals("table-a", ClientInteractionProxyRegistry.tableIdFor(1212, VIEWER_ID));
306+
assertEquals(1, coordinator.entityCount());
307+
}
308+
259309
@Test
260310
void cachedEntityCountTracksTheSameViewerAcrossMultipleRegions() {
261311
TableSessionContext session = mock(TableSessionContext.class);

0 commit comments

Comments
 (0)