mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-25 15:09:18 -07:00
[match] restrict matchmaking to event rooms only
This commit is contained in:
@@ -1070,7 +1070,8 @@ function roomInstanceFromRoom(
|
||||
isPrivate: boolean,
|
||||
instanceId: number,
|
||||
photonRoomId: string,
|
||||
subRoomId?: number
|
||||
subRoomId?: number,
|
||||
eventId = 0
|
||||
) {
|
||||
const f = instanceFieldsFromRoom(room, subRoomId)
|
||||
// The same region the connection info names its Photon apps for — see photonApps.
|
||||
@@ -1082,7 +1083,7 @@ function roomInstanceFromRoom(
|
||||
roomInstanceType: f.roomInstanceType,
|
||||
location: f.location,
|
||||
dataBlob: f.dataBlob,
|
||||
eventId: 0,
|
||||
eventId,
|
||||
clubId: 0,
|
||||
roomCode: '',
|
||||
photonRegion: region,
|
||||
@@ -1518,7 +1519,8 @@ async function resolveRoomInstance(
|
||||
isPrivate: boolean,
|
||||
ownerId: number,
|
||||
requestedSubRoomId?: number,
|
||||
gateOnAccessibility = false
|
||||
gateOnAccessibility = false,
|
||||
eventId = 0
|
||||
): Promise<ResolvedInstance> {
|
||||
const id = Number.parseInt(roomKey, 10)
|
||||
const requested = Number.isNaN(id)
|
||||
@@ -1606,6 +1608,10 @@ async function resolveRoomInstance(
|
||||
// reuses a public instance; an unpublished one reuses its single private session.
|
||||
// A private matchmake into a published room always gets a fresh instance. Create
|
||||
// one when there's nothing to join.
|
||||
//
|
||||
// An event's sessions are its own: an event matchmake reuses only an instance stamped
|
||||
// with that event (and stamps the one it creates), and a room matchmake — `eventId` 0
|
||||
// — never reuses an event's.
|
||||
let instance = unpublished
|
||||
? await getSharedPrivateInstance(
|
||||
c.env.DB,
|
||||
@@ -1616,7 +1622,14 @@ async function resolveRoomInstance(
|
||||
)
|
||||
: privateInstance
|
||||
? null
|
||||
: await getJoinableInstance(c.env.DB, f.roomId, gameVersion, f.subRoomId, currentInstanceId)
|
||||
: await getJoinableInstance(
|
||||
c.env.DB,
|
||||
f.roomId,
|
||||
gameVersion,
|
||||
f.subRoomId,
|
||||
currentInstanceId,
|
||||
eventId
|
||||
)
|
||||
if (!instance) {
|
||||
instance = await createRoomInstance(c.env.DB, {
|
||||
ownerAccountId: ownerId,
|
||||
@@ -1630,6 +1643,7 @@ async function resolveRoomInstance(
|
||||
isPrivate: privateInstance || f.isDorm,
|
||||
roomInstanceType: f.roomInstanceType,
|
||||
gameVersion,
|
||||
eventId,
|
||||
})
|
||||
}
|
||||
return {
|
||||
@@ -1639,7 +1653,8 @@ async function resolveRoomInstance(
|
||||
privateInstance,
|
||||
instance.roomInstanceId,
|
||||
instance.photonRoomId,
|
||||
f.subRoomId
|
||||
f.subRoomId,
|
||||
instance.eventId
|
||||
),
|
||||
errorCode: MatchmakingErrorCode.Success,
|
||||
}
|
||||
@@ -2428,12 +2443,16 @@ const app = new Hono<App>()
|
||||
}
|
||||
|
||||
const { joinMode, additionalPlayerIds } = await readMatchmakeBody(c)
|
||||
// The event id scopes the session: the caller joins an instance already stamped
|
||||
// with this event, or opens one that is — never the room's ordinary instances.
|
||||
const { instance, errorCode } = await resolveRoomInstance(
|
||||
c,
|
||||
String(event.RoomId),
|
||||
joinMode === 2,
|
||||
id,
|
||||
event.SubRoomId ?? undefined
|
||||
event.SubRoomId ?? undefined,
|
||||
false,
|
||||
eventId
|
||||
)
|
||||
if (!instance) return matchmakeResult(c, errorCode, null)
|
||||
await enterRoom(c, id, instance)
|
||||
@@ -2961,7 +2980,8 @@ const app = new Hono<App>()
|
||||
stored.isPrivate,
|
||||
stored.roomInstanceId,
|
||||
stored.photonRoomId,
|
||||
stored.subRoomId
|
||||
stored.subRoomId,
|
||||
stored.eventId
|
||||
)
|
||||
await enterRoom(c, id, instance)
|
||||
return matchmakeResult(c, 0, instance)
|
||||
|
||||
@@ -1071,28 +1071,70 @@ describe('public endpoints', () => {
|
||||
})
|
||||
type Body = {
|
||||
errorCode: number
|
||||
roomInstance: { roomId: number; location: string; roomInstanceId: number } | null
|
||||
roomInstance: {
|
||||
roomId: number
|
||||
location: string
|
||||
roomInstanceId: number
|
||||
eventId: number
|
||||
} | null
|
||||
}
|
||||
const join = async (path: string, sub?: string) =>
|
||||
(await (await matchmake(path, sub)).json()) as Body
|
||||
|
||||
// An invited player lands in an instance of the event's room (2)...
|
||||
// An invited player lands in an instance of the event's room (2), which names the
|
||||
// event it is for...
|
||||
const invited = await join('/matchmake/event/8', '301')
|
||||
expect(invited.errorCode).toBe(0)
|
||||
expect(invited.roomInstance).toMatchObject({ roomId: 2, location: RECCENTER_SCENE })
|
||||
expect(invited.roomInstance).toMatchObject({
|
||||
roomId: 2,
|
||||
location: RECCENTER_SCENE,
|
||||
eventId: 8,
|
||||
})
|
||||
|
||||
// ...recorded as their presence, like any other matchmake.
|
||||
const row = await env.DB.prepare('SELECT data FROM presence WHERE account_id = ?1')
|
||||
.bind(301)
|
||||
.first<{ data: string }>()
|
||||
const presence = JSON.parse(row!.data) as { roomInstance: { roomInstanceId: number } }
|
||||
const presence = JSON.parse(row!.data) as {
|
||||
roomInstance: { roomInstanceId: number; eventId: number }
|
||||
}
|
||||
expect(presence.roomInstance.roomInstanceId).toBe(invited.roomInstance!.roomInstanceId)
|
||||
expect(presence.roomInstance.eventId).toBe(8)
|
||||
|
||||
// The instance itself is stamped, which is what the next guest's search matches on.
|
||||
const stored = await env.DB.prepare('SELECT event_id FROM room_instance WHERE id = ?1')
|
||||
.bind(invited.roomInstance!.roomInstanceId)
|
||||
.first<{ event_id: number }>()
|
||||
expect(stored!.event_id).toBe(8)
|
||||
|
||||
// The creator gets in, and so does someone who was invited and DECLINED — the row
|
||||
// is the invite, whatever the answer.
|
||||
expect((await join('/matchmake/event/8', '300')).errorCode).toBe(0)
|
||||
// is the invite, whatever the answer. They join the SAME session: the event's.
|
||||
const creator = await join('/matchmake/event/8', '300')
|
||||
expect(creator.errorCode).toBe(0)
|
||||
expect(creator.roomInstance).toMatchObject({
|
||||
roomInstanceId: invited.roomInstance!.roomInstanceId,
|
||||
eventId: 8,
|
||||
})
|
||||
expect((await join('/matchmake/event/8', '302')).errorCode).toBe(0)
|
||||
|
||||
// A plain matchmake into the same room stays out of the event's session, and
|
||||
// another event in that room gets a session of its own.
|
||||
const plain = await join('/matchmake/room/2', '399')
|
||||
expect(plain.errorCode).toBe(0)
|
||||
expect(plain.roomInstance!.eventId).toBe(0)
|
||||
expect(plain.roomInstance!.roomInstanceId).not.toBe(invited.roomInstance!.roomInstanceId)
|
||||
const other = await join('/matchmake/event/9', '303')
|
||||
expect(other.roomInstance!.eventId).toBe(9)
|
||||
expect(other.roomInstance!.roomInstanceId).not.toBe(invited.roomInstance!.roomInstanceId)
|
||||
|
||||
// Coming back to the event from elsewhere lands in the event's session again, not
|
||||
// the room's ordinary one.
|
||||
expect((await join('/matchmake/room/2', '300')).roomInstance!.eventId).toBe(0)
|
||||
expect((await join('/matchmake/event/8', '300')).roomInstance).toMatchObject({
|
||||
roomInstanceId: invited.roomInstance!.roomInstanceId,
|
||||
eventId: 8,
|
||||
})
|
||||
|
||||
// A stranger doesn't — and is told why (35 EventIsPrivate), not fobbed off with 20.
|
||||
expect(await join('/matchmake/event/8', '399')).toEqual(refused(35))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user