Skip to content

Commit 8456926

Browse files
authored
Merge pull request #117 from EllanServer/codex/perf-ray-proxy-stable-plan
perf(render): cache stable ray proxy identities
2 parents 8d65030 + ef9ec5e commit 8456926

2 files changed

Lines changed: 62 additions & 18 deletions

File tree

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

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,23 +75,21 @@ synchronized void replace(
7575
if (viewerId == null || interactions == null || interactions.isEmpty()) {
7676
continue;
7777
}
78-
Player viewer = this.session.onlinePlayer(viewerId);
79-
if (viewer == null || !viewer.isOnline()) {
80-
continue;
81-
}
8278
ActiveProxies current = previous.get(viewerId);
83-
if (this.canReuse(viewerId, viewer, current, interactions)) {
79+
if (current != null
80+
&& current.viewer().isOnline()
81+
&& this.canReuse(viewerId, current, interactions)) {
8482
next.put(viewerId, current);
8583
continue;
8684
}
85+
Player viewer = this.session.onlinePlayer(viewerId);
86+
if (viewer == null || !viewer.isOnline()) {
87+
continue;
88+
}
8789
List<InteractionGeometry> geometry = interactionGeometry(interactions);
8890
List<ClientProxy> proxies = this.backend.create(viewer, interactions);
8991
if (!proxies.isEmpty()) {
90-
ActiveProxies created = new ActiveProxies(
91-
viewer,
92-
List.copyOf(proxies),
93-
geometry
94-
);
92+
ActiveProxies created = new ActiveProxies(viewer, List.copyOf(proxies), geometry);
9593
next.put(viewerId, created);
9694
pendingSpawns.add(new PendingSpawn(viewerId, created));
9795
}
@@ -147,12 +145,10 @@ private boolean canReuseRegion(
147145
if (viewerId == null || interactions == null || interactions.isEmpty()) {
148146
continue;
149147
}
150-
Player viewer = this.session.onlinePlayer(viewerId);
151-
if (viewer == null || !viewer.isOnline()) {
152-
continue;
153-
}
154148
ActiveProxies active = previous.get(viewerId);
155-
if (!this.canReuse(viewerId, viewer, active, interactions)) {
149+
if (active == null
150+
|| !active.viewer().isOnline()
151+
|| !this.canReuse(viewerId, active, interactions)) {
156152
return false;
157153
}
158154
reusableViewers++;
@@ -162,12 +158,10 @@ private boolean canReuseRegion(
162158

163159
private boolean canReuse(
164160
UUID viewerId,
165-
Player viewer,
166161
ActiveProxies active,
167162
List<DisplayInteractionRayRegistry.RayInteraction> interactions
168163
) {
169164
if (active == null
170-
|| active.viewer() != viewer
171165
|| !sameGeometry(active.geometry(), interactions)
172166
|| active.proxies().isEmpty()) {
173167
return false;

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

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ void clearedViewerOwnershipMakesAnUnchangedRegionStaleForReconnect() {
123123
}
124124

125125
@Test
126-
void unchangedGeometryReusesClientProxyWithoutMorePackets() {
126+
void unchangedGeometryReusesClientProxyWithoutMorePacketsOrViewerLookups() {
127127
TableSessionContext session = mock(TableSessionContext.class);
128128
SparrowRayInteractionProxyCoordinator.Backend backend = mock(
129129
SparrowRayInteractionProxyCoordinator.Backend.class
@@ -153,6 +153,56 @@ void unchangedGeometryReusesClientProxyWithoutMorePackets() {
153153
verify(backend, times(1)).create(eq(viewer), any());
154154
verify(backend, times(1)).spawn(viewer, List.of(proxy));
155155
verify(backend, never()).destroy(viewer, List.of(proxy));
156+
verify(session, times(1)).onlinePlayer(VIEWER_ID);
157+
assertEquals(1, coordinator.entityCount());
158+
}
159+
160+
@Test
161+
void offlineCachedViewerUsesFreshSessionPlayer() {
162+
TableSessionContext session = mock(TableSessionContext.class);
163+
SparrowRayInteractionProxyCoordinator.Backend backend = mock(
164+
SparrowRayInteractionProxyCoordinator.Backend.class
165+
);
166+
SparrowRayInteractionProxyCoordinator.ClientProxy firstProxy = mock(
167+
SparrowRayInteractionProxyCoordinator.ClientProxy.class
168+
);
169+
SparrowRayInteractionProxyCoordinator.ClientProxy reconnectedProxy = mock(
170+
SparrowRayInteractionProxyCoordinator.ClientProxy.class
171+
);
172+
Player firstViewer = mock(Player.class);
173+
Player reconnectedViewer = mock(Player.class);
174+
when(session.id()).thenReturn("table-a");
175+
when(session.onlinePlayer(VIEWER_ID)).thenReturn(firstViewer).thenReturn(reconnectedViewer);
176+
when(firstViewer.isOnline()).thenReturn(true);
177+
when(reconnectedViewer.isOnline()).thenReturn(true);
178+
when(backend.available()).thenReturn(true);
179+
when(backend.create(eq(firstViewer), any())).thenReturn(List.of(firstProxy));
180+
when(backend.create(eq(reconnectedViewer), any())).thenReturn(List.of(reconnectedProxy));
181+
when(firstProxy.entityId()).thenReturn(1205);
182+
when(reconnectedProxy.entityId()).thenReturn(1206);
183+
doAnswer(invocation -> {
184+
invocation.<Runnable>getArgument(1).run();
185+
return null;
186+
}).when(session).runForViewer(any(Player.class), any(Runnable.class));
187+
SparrowRayInteractionProxyCoordinator coordinator = new SparrowRayInteractionProxyCoordinator(
188+
session,
189+
backend
190+
);
191+
Map<UUID, List<DisplayInteractionRayRegistry.RayInteraction>> interactions = Map.of(
192+
VIEWER_ID,
193+
List.of(interaction())
194+
);
195+
196+
coordinator.replace("actions", interactions);
197+
when(firstViewer.isOnline()).thenReturn(false);
198+
coordinator.replace("actions", interactions);
199+
200+
verify(backend).spawn(firstViewer, List.of(firstProxy));
201+
verify(backend).spawn(reconnectedViewer, List.of(reconnectedProxy));
202+
verify(backend, never()).destroy(firstViewer, List.of(firstProxy));
203+
verify(session, times(2)).onlinePlayer(VIEWER_ID);
204+
assertNull(ClientInteractionProxyRegistry.tableIdFor(1205, VIEWER_ID));
205+
assertEquals("table-a", ClientInteractionProxyRegistry.tableIdFor(1206, VIEWER_ID));
156206
assertEquals(1, coordinator.entityCount());
157207
}
158208

0 commit comments

Comments
 (0)