Skip to content

Commit fbf3953

Browse files
perf(render): reuse serialized viewer ids (#68)
* perf(render): reuse serialized viewer ids * style(render): satisfy Kotlin formatting gate --------- Co-authored-by: Arbousier1 <elderli@foxmail.com>
1 parent 6283522 commit fbf3953

2 files changed

Lines changed: 65 additions & 23 deletions

File tree

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

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import top.ellan.mahjong.render.TableRenderSubject;
55
import top.ellan.mahjong.render.snapshot.TableRenderSnapshot;
66
import top.ellan.mahjong.render.snapshot.TableSeatRenderSnapshot;
7+
import java.util.Comparator;
78
import java.util.EnumMap;
89
import java.util.HashMap;
910
import java.util.HashSet;
@@ -12,24 +13,31 @@
1213
import java.util.Objects;
1314
import java.util.Set;
1415
import java.util.UUID;
15-
import java.util.Comparator;
1616
import org.bukkit.Location;
1717
import org.bukkit.entity.Player;
1818

1919
public final class TableRenderSnapshotFactory {
2020
public TableRenderSnapshot create(TableRenderSubject session, long version, long cancellationNonce) {
2121
Location tableCenter = session.center();
2222
boolean started = session.isStarted();
23-
List<UUID> onlineViewerIds = session.viewers().stream()
23+
List<SerializedViewerId> serializedOnlineViewerIds = session.viewers().stream()
2424
.map(Player::getUniqueId)
2525
.distinct()
26-
.sorted(Comparator.comparing(UUID::toString))
26+
.map(viewerId -> new SerializedViewerId(viewerId, viewerId.toString()))
27+
.sorted(Comparator.comparing(SerializedViewerId::serializedId))
28+
.toList();
29+
List<UUID> onlineViewerIds = serializedOnlineViewerIds.stream()
30+
.map(SerializedViewerId::id)
2731
.toList();
2832
Set<UUID> onlineViewerIdSet = new HashSet<>(onlineViewerIds);
2933
Map<UUID, String> viewerMembershipSignatures = new HashMap<>();
3034
Map<UUID, List<UUID>> viewerIdsExcluding = new HashMap<>();
31-
for (UUID viewerId : onlineViewerIds) {
32-
viewerMembershipSignatures.put(viewerId, this.viewerMembershipSignature(onlineViewerIds, viewerId));
35+
for (SerializedViewerId viewer : serializedOnlineViewerIds) {
36+
UUID viewerId = viewer.id();
37+
viewerMembershipSignatures.put(
38+
viewerId,
39+
this.viewerMembershipSignature(serializedOnlineViewerIds, viewerId)
40+
);
3341
viewerIdsExcluding.put(viewerId, this.viewerIdsExcluding(onlineViewerIds, viewerId));
3442
}
3543
EnumMap<SeatWind, TableSeatRenderSnapshot> seats = new EnumMap<>(SeatWind.class);
@@ -130,11 +138,11 @@ private TableSeatRenderSnapshot captureSeatSnapshot(
130138
);
131139
}
132140

133-
private String viewerMembershipSignature(List<UUID> onlineViewerIds, UUID excludedPlayerId) {
141+
private String viewerMembershipSignature(List<SerializedViewerId> onlineViewerIds, UUID excludedPlayerId) {
134142
StringBuilder builder = new StringBuilder(onlineViewerIds.size() * 36);
135-
for (UUID viewerId : onlineViewerIds) {
136-
if (!viewerId.equals(excludedPlayerId)) {
137-
builder.append(viewerId);
143+
for (SerializedViewerId viewer : onlineViewerIds) {
144+
if (!viewer.id().equals(excludedPlayerId)) {
145+
builder.append(viewer.serializedId());
138146
}
139147
}
140148
return builder.toString();
@@ -145,4 +153,6 @@ private List<UUID> viewerIdsExcluding(List<UUID> onlineViewerIds, UUID excludedP
145153
.filter(viewerId -> !viewerId.equals(excludedPlayerId))
146154
.toList();
147155
}
156+
157+
private record SerializedViewerId(UUID id, String serializedId) {}
148158
}

src/test/kotlin/top/ellan/mahjong/table/render/TableRenderSnapshotFactoryTest.kt

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
package top.ellan.mahjong.table.render
22

3-
import top.ellan.mahjong.model.SeatWind
4-
import top.ellan.mahjong.render.scene.MeldView
5-
import top.ellan.mahjong.table.core.MahjongTableSession
63
import org.bukkit.Location
74
import org.bukkit.entity.Player
85
import org.mockito.ArgumentMatchers
@@ -11,26 +8,39 @@ import org.mockito.Mockito.mock
118
import org.mockito.Mockito.never
129
import org.mockito.Mockito.verify
1310
import org.mockito.Mockito.`when`
11+
import top.ellan.mahjong.model.SeatWind
12+
import top.ellan.mahjong.render.scene.MeldView
13+
import top.ellan.mahjong.table.core.MahjongTableSession
1414
import java.util.UUID
1515
import kotlin.test.Test
1616
import kotlin.test.assertEquals
1717
import kotlin.test.assertTrue
1818

1919
class TableRenderSnapshotFactoryTest {
2020
@Test
21-
fun `render snapshot reuses precomputed online viewers`() {
21+
fun `render snapshot preserves serialized viewer ordering and membership`() {
2222
val session = mock(MahjongTableSession::class.java)
2323
val factory = TableRenderSnapshotFactory()
2424

2525
val eastId = UUID.fromString("00000000-0000-0000-0000-000000000011")
26-
val southId = UUID.fromString("00000000-0000-0000-0000-000000000012")
26+
val southId = UUID.fromString("80000000-0000-0000-0000-000000000012")
27+
val westId = UUID.fromString("7fffffff-ffff-ffff-ffff-ffffffffffff")
28+
val northId = UUID.fromString("00000000-0000-0000-0000-000000000014")
2729
val eastViewer = mock(Player::class.java)
30+
val duplicateEastViewer = mock(Player::class.java)
2831
val southViewer = mock(Player::class.java)
32+
val westViewer = mock(Player::class.java)
2933

34+
assertTrue(southId.compareTo(eastId) < 0)
35+
assertTrue(southId.toString().compareTo(eastId.toString()) > 0)
3036
`when`(eastViewer.uniqueId).thenReturn(eastId)
37+
`when`(duplicateEastViewer.uniqueId).thenReturn(eastId)
3138
`when`(southViewer.uniqueId).thenReturn(southId)
39+
`when`(westViewer.uniqueId).thenReturn(westId)
3240
`when`(session.center()).thenReturn(Location(null, 0.0, 64.0, 0.0))
33-
`when`(session.viewers()).thenReturn(listOf(eastViewer, southViewer))
41+
`when`(session.viewers()).thenReturn(
42+
listOf(southViewer, westViewer, duplicateEastViewer, eastViewer),
43+
)
3444
`when`(session.isStarted()).thenReturn(false)
3545
`when`(session.isRoundFinished()).thenReturn(false)
3646
`when`(session.remainingWallCount()).thenReturn(0)
@@ -49,8 +59,8 @@ class TableRenderSnapshotFactoryTest {
4959
`when`(session.doraIndicators()).thenReturn(emptyList())
5060
`when`(session.playerAt(SeatWind.EAST)).thenReturn(eastId)
5161
`when`(session.playerAt(SeatWind.SOUTH)).thenReturn(southId)
52-
`when`(session.playerAt(SeatWind.WEST)).thenReturn(null)
53-
`when`(session.playerAt(SeatWind.NORTH)).thenReturn(null)
62+
`when`(session.playerAt(SeatWind.WEST)).thenReturn(westId)
63+
`when`(session.playerAt(SeatWind.NORTH)).thenReturn(northId)
5464

5565
doAnswer { invocation ->
5666
(invocation.arguments[0] as UUID?)?.toString() ?: ""
@@ -61,7 +71,7 @@ class TableRenderSnapshotFactoryTest {
6171
`when`(session.stickLayoutCount(wind)).thenReturn(0)
6272
`when`(session.cornerSticks(wind)).thenReturn(emptyList())
6373
}
64-
for (playerId in listOf(eastId, southId)) {
74+
for (playerId in listOf(eastId, southId, westId, northId)) {
6575
`when`(session.points(playerId)).thenReturn(25000)
6676
`when`(session.isRiichi(playerId)).thenReturn(false)
6777
`when`(session.isReady(playerId)).thenReturn(false)
@@ -77,13 +87,30 @@ class TableRenderSnapshotFactoryTest {
7787
val snapshot = factory.create(session, 1L, 0L)
7888
val eastSeat = snapshot.seat(SeatWind.EAST)
7989
val southSeat = snapshot.seat(SeatWind.SOUTH)
90+
val westSeat = snapshot.seat(SeatWind.WEST)
91+
val northSeat = snapshot.seat(SeatWind.NORTH)
8092

8193
assertTrue(eastSeat.online())
8294
assertTrue(southSeat.online())
83-
assertEquals(listOf(southId), eastSeat.viewerIdsExcluding())
84-
assertEquals(listOf(eastId), southSeat.viewerIdsExcluding())
85-
assertEquals(southId.toString(), eastSeat.viewerMembershipSignature())
86-
assertEquals(eastId.toString(), southSeat.viewerMembershipSignature())
95+
assertTrue(westSeat.online())
96+
assertTrue(!northSeat.online())
97+
assertEquals(listOf(westId, southId), eastSeat.viewerIdsExcluding())
98+
assertEquals(listOf(eastId, westId), southSeat.viewerIdsExcluding())
99+
assertEquals(listOf(eastId, southId), westSeat.viewerIdsExcluding())
100+
assertTrue(northSeat.viewerIdsExcluding().isEmpty())
101+
assertEquals(
102+
"7fffffff-ffff-ffff-ffff-ffffffffffff80000000-0000-0000-0000-000000000012",
103+
eastSeat.viewerMembershipSignature(),
104+
)
105+
assertEquals(
106+
"00000000-0000-0000-0000-0000000000117fffffff-ffff-ffff-ffff-ffffffffffff",
107+
southSeat.viewerMembershipSignature(),
108+
)
109+
assertEquals(
110+
"00000000-0000-0000-0000-00000000001180000000-0000-0000-0000-000000000012",
111+
westSeat.viewerMembershipSignature(),
112+
)
113+
assertTrue(northSeat.viewerMembershipSignature().isEmpty())
87114

88115
verify(session, never()).onlinePlayer(ArgumentMatchers.any(UUID::class.java))
89116
verify(session, never()).viewerIdsExcluding(ArgumentMatchers.any(UUID::class.java))
@@ -123,7 +150,12 @@ class TableRenderSnapshotFactoryTest {
123150
val snapshot = factory.create(session, 1L, 0L)
124151

125152
assertTrue(snapshot.doraIndicators().isEmpty())
153+
for (wind in SeatWind.values()) {
154+
val seat = snapshot.seat(wind)
155+
assertTrue(!seat.online())
156+
assertTrue(seat.viewerIdsExcluding().isEmpty())
157+
assertTrue(seat.viewerMembershipSignature().isEmpty())
158+
}
126159
verify(session, never()).doraIndicators()
127160
}
128161
}
129-

0 commit comments

Comments
 (0)