Skip to content

Commit 2fd94a8

Browse files
committed
Don't update clan count when polling game server
1 parent 3bdc3ad commit 2fd94a8

File tree

2 files changed

+27
-172
lines changed

2 files changed

+27
-172
lines changed

apps/worker/src/workers/pollGameServer.test.ts

Lines changed: 11 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { prismaMock } from "../../test/mockPrisma";
22
import { ServerHeader } from "../packet";
33
import { GameServerInfoPacket } from "../packets/gameServerInfo";
44
import { GameServer, GameServerSnapshot, GameServerState, Player } from "@prisma/client";
5-
import { changePlayerClans, processGameServerInfo } from "./pollGameServer";
5+
import { processGameServerInfo } from "./pollGameServer";
66

77
const newGameServer = (): GameServer & { gameServerState: GameServerState | null } => ({
88
id: 1,
@@ -68,29 +68,24 @@ test('processGameServerInfo', async () => {
6868
id: 1,
6969
} as unknown as GameServerSnapshot);
7070

71-
prismaMock.player.findUnique.mockResolvedValue({ clanName: null } as unknown as Player);
72-
prismaMock.player.updateMany.mockResolvedValue({ count: 1 });
73-
7471
await processGameServerInfo(gameServer, gameServerInfo);
7572

7673
expect(prismaMock.clan.createMany).toHaveBeenCalledWith({
7774
data: [{ name: 'clan1' }, { name: 'clan2' }],
7875
skipDuplicates: true
7976
});
8077

81-
expect(prismaMock.player.createMany).toHaveBeenCalledWith({
82-
data: [{ name: 'name1' }, { name: 'name2' }],
83-
skipDuplicates: true
84-
});
78+
expect(prismaMock.player.upsert).toHaveBeenCalledWith(expect.objectContaining({
79+
where: { name: 'name1' },
80+
update: { clanName: 'clan1', lastSeenAt: expect.any(Date) },
81+
create: { name: 'name1', clanName: 'clan1', lastSeenAt: expect.any(Date) },
82+
}));
8583

86-
expect(prismaMock.player.updateMany).toHaveBeenCalledWith({
87-
where: { name: 'name1', clanName: null },
88-
data: { clanName: 'clan1' }
89-
});
90-
expect(prismaMock.player.updateMany).toHaveBeenCalledWith({
91-
where: { name: 'name2', clanName: null },
92-
data: { clanName: 'clan2' }
93-
});
84+
expect(prismaMock.player.upsert).toHaveBeenCalledWith(expect.objectContaining({
85+
where: { name: 'name2' },
86+
update: { clanName: 'clan2', lastSeenAt: expect.any(Date) },
87+
create: { name: 'name2', clanName: 'clan2', lastSeenAt: expect.any(Date) },
88+
}));
9489

9590
expect(prismaMock.gameServerSnapshot.create).toHaveBeenCalledWith(expect.objectContaining({
9691
data: expect.objectContaining({
@@ -117,78 +112,3 @@ test('processGameServerInfo', async () => {
117112
})
118113
}));
119114
});
120-
121-
describe('changePlayerClan', () => {
122-
123-
test('Empty clan', async () => {
124-
prismaMock.player.findUnique.mockResolvedValue({ clanName: 'clan1' } as unknown as Player);
125-
prismaMock.player.updateMany.mockResolvedValue({ count: 1 });
126-
127-
await changePlayerClans({
128-
'name1': null,
129-
});
130-
131-
expect(prismaMock.player.updateMany).toHaveBeenCalledWith({
132-
where: { name: 'name1', clanName: 'clan1' },
133-
data: { clanName: null }
134-
});
135-
136-
expect(prismaMock.clan.update).toHaveBeenCalledWith({
137-
where: { name: 'clan1' },
138-
data: { activePlayerCount: { increment: -1 } }
139-
});
140-
});
141-
142-
test('Clan changes', async () => {
143-
prismaMock.player.findUnique.mockResolvedValue({ clanName: 'clan1' } as unknown as Player);
144-
prismaMock.player.updateMany.mockResolvedValue({ count: 1 });
145-
146-
await changePlayerClans({
147-
'name1': 'clan2',
148-
});
149-
150-
expect(prismaMock.player.updateMany).toHaveBeenCalledWith({
151-
where: { name: 'name1', clanName: 'clan1' },
152-
data: { clanName: 'clan2' }
153-
});
154-
155-
expect(prismaMock.clan.update).toHaveBeenCalledWith({
156-
where: { name: 'clan1' },
157-
data: { activePlayerCount: { increment: -1 } }
158-
});
159-
160-
expect(prismaMock.clan.update).toHaveBeenCalledWith({
161-
where: { name: 'clan2' },
162-
data: { activePlayerCount: { increment: 1 } }
163-
});
164-
});
165-
166-
test('Clan changes with race condition', async () => {
167-
prismaMock.player.findUnique.mockResolvedValueOnce({ clanName: 'clan1' } as unknown as Player);
168-
prismaMock.player.findUnique.mockResolvedValueOnce({ clanName: 'clan1-bis' } as unknown as Player);
169-
prismaMock.player.updateMany.mockResolvedValueOnce({ count: 0 });
170-
prismaMock.player.updateMany.mockResolvedValueOnce({ count: 1 });
171-
172-
await changePlayerClans({
173-
'name1': 'clan2',
174-
});
175-
176-
expect(prismaMock.player.updateMany).toHaveBeenCalledWith({
177-
where: { name: 'name1', clanName: 'clan1' },
178-
data: { clanName: 'clan2' }
179-
});
180-
expect(prismaMock.player.updateMany).toHaveBeenCalledWith({
181-
where: { name: 'name1', clanName: 'clan1-bis' },
182-
data: { clanName: 'clan2' }
183-
});
184-
185-
expect(prismaMock.clan.update).toHaveBeenCalledWith({
186-
where: { name: 'clan1-bis' },
187-
data: { activePlayerCount: { increment: -1 } }
188-
});
189-
expect(prismaMock.clan.update).toHaveBeenCalledWith({
190-
where: { name: 'clan2' },
191-
data: { activePlayerCount: { increment: 1 } }
192-
});
193-
});
194-
});

apps/worker/src/workers/pollGameServer.ts

Lines changed: 16 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -48,66 +48,6 @@ function skipPolling(gameServer: GameServer & { gameServerState: GameServerState
4848
return Math.random() >= (1.0 / Math.min(gameServer.failureCount, MAX_FAILURE_COUNT));
4949
}
5050

51-
export async function changePlayerClans(
52-
playerClans: Record<string, string | null>,
53-
) {
54-
const totalPlayerCount = Object.keys(playerClans).length;
55-
const clanDelta: Record<string, number> = {};
56-
57-
// When changing clans, clan `activePlayerCount` needs to be updated for both
58-
// the old and new clans. To avoid race conditions, when updating a player's
59-
// clan, make sure old player clan is accurate.
60-
61-
for (let i = 0; i < 10; i++) {
62-
for (const [playerName, newClanName_] of Object.entries(playerClans)) {
63-
const newClanName = newClanName_ || null;
64-
65-
const currentPlayer = await prisma.player.findUnique({
66-
where: { name: playerName },
67-
select: { clanName: true },
68-
});
69-
70-
const oldClanName = currentPlayer?.clanName || null;
71-
72-
const ret = await prisma.player.updateMany({
73-
where: { name: playerName, clanName: oldClanName },
74-
data: { clanName: newClanName },
75-
});
76-
77-
if (ret.count !== 0) {
78-
if (oldClanName !== null) {
79-
clanDelta[oldClanName] = (clanDelta[oldClanName] ?? 0) - 1;
80-
}
81-
82-
if (newClanName !== null) {
83-
clanDelta[newClanName] = (clanDelta[newClanName] ?? 0) + 1;
84-
}
85-
86-
delete playerClans[playerName];
87-
}
88-
}
89-
90-
const remainingPlayerCount = Object.keys(playerClans).length;
91-
if (remainingPlayerCount === 0) {
92-
break;
93-
}
94-
}
95-
96-
const remainingPlayerCount = Object.keys(playerClans).length;
97-
if (remainingPlayerCount > 0) {
98-
console.error(`${remainingPlayerCount}/${totalPlayerCount} players failed to update`);
99-
}
100-
101-
for (const [clanName, delta] of Object.entries(clanDelta)) {
102-
if (delta !== 0) {
103-
await prisma.clan.update({
104-
where: { name: clanName },
105-
data: { activePlayerCount: { increment: delta } },
106-
});
107-
}
108-
}
109-
}
110-
11151
export async function processGameServerInfo(
11252
gameServer: GameServer & { gameServerState: GameServerState | null },
11353
gameServerInfo: GameServerInfoPacket,
@@ -149,16 +89,22 @@ export async function processGameServerInfo(
14989

15090
const uniqClients = uniqBy(gameServerInfo.clients, 'name');
15191

152-
await prisma.player.createMany({
153-
data: uniqClients.map((client) => ({
154-
name: client.name,
155-
})),
156-
skipDuplicates: true,
157-
});
158-
159-
await changePlayerClans(
160-
Object.fromEntries(uniqClients.map((client) => [client.name, client.clan])),
161-
);
92+
for (const client of uniqClients) {
93+
await prisma.player.upsert({
94+
where: {
95+
name: client.name,
96+
},
97+
update: {
98+
clanName: client.clan === "" ? undefined : client.clan,
99+
lastSeenAt: new Date()
100+
},
101+
create: {
102+
name: client.name,
103+
clanName: client.clan === "" ? undefined : client.clan,
104+
lastSeenAt: new Date(),
105+
},
106+
});
107+
}
162108

163109
for (const client of uniqClients) {
164110
await prisma.playerInfoGameType.upsert({
@@ -280,17 +226,6 @@ export async function processGameServerInfo(
280226
},
281227
});
282228

283-
for (const client of uniqClients) {
284-
await prisma.player.update({
285-
where: {
286-
name: client.name,
287-
},
288-
data: {
289-
lastSeenAt: new Date(),
290-
},
291-
});
292-
}
293-
294229
await prisma.gameServer.update({
295230
where: {
296231
id: gameServer.id,

0 commit comments

Comments
 (0)