mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-25 23:18:51 -07:00
[rooms] unset cloning on copy
This commit is contained in:
@@ -1719,7 +1719,7 @@ const app = new Hono<App>()
|
|||||||
'caller. Cloning is the only way to make a room, so the per-account room cap is',
|
'caller. Cloning is the only way to make a room, so the per-account room cap is',
|
||||||
'enforced here — it counts the rooms the account created, minus their auto-provisioned',
|
'enforced here — it counts the rooms the account created, minus their auto-provisioned',
|
||||||
'dorm (`MAX_ROOMS_PER_ACCOUNT`; 0 lifts the cap). The clone starts with no tags,',
|
'dorm (`MAX_ROOMS_PER_ACCOUNT`; 0 lifts the cap). The clone starts with no tags,',
|
||||||
'`IsRRO` cleared, and PRIVATE accessibility — a new room is unpublished until its',
|
'zeroed `Stats`, `IsRRO`/`IsDeveloperOwned`/`CloningAllowed` cleared, and PRIVATE accessibility — a new room is unpublished until its',
|
||||||
'owner sets its accessibility, so it never lands in the public feeds on creation.',
|
'owner sets its accessibility, so it never lands in the public feeds on creation.',
|
||||||
'',
|
'',
|
||||||
'Rejections — a blank or taken name, the cap, a source that disallows cloning — are',
|
'Rejections — a blank or taken name, the cap, a source that disallows cloning — are',
|
||||||
|
|||||||
@@ -1309,11 +1309,20 @@ describe('rooms endpoints', () => {
|
|||||||
CreatorAccountId: number
|
CreatorAccountId: number
|
||||||
Tags?: Array<{ Tag: string }>
|
Tags?: Array<{ Tag: string }>
|
||||||
IsRRO: boolean
|
IsRRO: boolean
|
||||||
|
IsDeveloperOwned: boolean
|
||||||
|
CloningAllowed: boolean
|
||||||
Accessibility: number
|
Accessibility: number
|
||||||
Roles: Array<{ AccountId: number; Role: number; InvitedRole: number }>
|
Roles: Array<{ AccountId: number; Role: number; InvitedRole: number }>
|
||||||
|
Stats: Record<string, number>
|
||||||
} | null
|
} | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Give the source every flag and counter the clone must NOT inherit.
|
||||||
|
await env.DB.prepare(
|
||||||
|
`UPDATE room SET data = json_set(data, '$.IsDeveloperOwned', json('true'),
|
||||||
|
'$.CloningAllowed', json('true'), '$.Stats.VisitorCount', 42) WHERE room_id = 24`
|
||||||
|
).run()
|
||||||
|
|
||||||
// Clone MakerRoom (base, RoomId 24) → a fresh room owned by the caller (801).
|
// Clone MakerRoom (base, RoomId 24) → a fresh room owned by the caller (801).
|
||||||
const ok = await post(24, 'MyMakerClone')
|
const ok = await post(24, 'MyMakerClone')
|
||||||
expect(ok.success).toBe(true)
|
expect(ok.success).toBe(true)
|
||||||
@@ -1327,6 +1336,16 @@ describe('rooms endpoints', () => {
|
|||||||
expect(ok.value!.Tags).toEqual([])
|
expect(ok.value!.Tags).toEqual([])
|
||||||
// IsRRO is cleared so the client doesn't render a virtual "RRO" tag on the clone.
|
// IsRRO is cleared so the client doesn't render a virtual "RRO" tag on the clone.
|
||||||
expect(ok.value!.IsRRO).toBe(false)
|
expect(ok.value!.IsRRO).toBe(false)
|
||||||
|
// Nor is it developer-owned, and the new owner opts it into cloning themselves.
|
||||||
|
expect(ok.value!.IsDeveloperOwned).toBe(false)
|
||||||
|
expect(ok.value!.CloningAllowed).toBe(false)
|
||||||
|
// No engagement carries over — not even the blob-stored VisitorCount.
|
||||||
|
expect(ok.value!.Stats).toEqual({
|
||||||
|
CheerCount: 0,
|
||||||
|
FavoriteCount: 0,
|
||||||
|
VisitorCount: 0,
|
||||||
|
VisitCount: 0,
|
||||||
|
})
|
||||||
// A new room is unpublished: Private (0), never the source's visibility.
|
// A new room is unpublished: Private (0), never the source's visibility.
|
||||||
expect(ok.value!.Accessibility).toBe(0)
|
expect(ok.value!.Accessibility).toBe(0)
|
||||||
// Ownership is reset to the cloner: sole owner (Role 255), and none of the
|
// Ownership is reset to the cloner: sole owner (Role 255), and none of the
|
||||||
@@ -1338,8 +1357,16 @@ describe('rooms endpoints', () => {
|
|||||||
// It persists and is fetchable by its new id.
|
// It persists and is fetchable by its new id.
|
||||||
const fetched = (await (await SELF.fetch(`${ORIGIN}/rooms/${ok.value!.RoomId}`)).json()) as {
|
const fetched = (await (await SELF.fetch(`${ORIGIN}/rooms/${ok.value!.RoomId}`)).json()) as {
|
||||||
Name: string
|
Name: string
|
||||||
|
Tags: unknown[]
|
||||||
|
IsDeveloperOwned: boolean
|
||||||
|
CloningAllowed: boolean
|
||||||
|
Stats: Record<string, number>
|
||||||
}
|
}
|
||||||
expect(fetched.Name).toBe('MyMakerClone')
|
expect(fetched.Name).toBe('MyMakerClone')
|
||||||
|
expect(fetched.Tags).toEqual([])
|
||||||
|
expect(fetched.IsDeveloperOwned).toBe(false)
|
||||||
|
expect(fetched.CloningAllowed).toBe(false)
|
||||||
|
expect(fetched.Stats.VisitorCount).toBe(0)
|
||||||
|
|
||||||
// Duplicate name is rejected.
|
// Duplicate name is rejected.
|
||||||
const dup = await post(24, 'MyMakerClone')
|
const dup = await post(24, 'MyMakerClone')
|
||||||
|
|||||||
@@ -661,8 +661,8 @@ export async function deleteRoomLeaderboard(
|
|||||||
* name, and the new owner. The clone starts with an empty tag set — the source's
|
* name, and the new owner. The clone starts with an empty tag set — the source's
|
||||||
* tags (including the `base` template tag) do not carry over, so the owner tags the
|
* tags (including the `base` template tag) do not carry over, so the owner tags the
|
||||||
* clone from scratch — `IsRRO` is cleared so the client doesn't render a virtual
|
* clone from scratch — `IsRRO` is cleared so the client doesn't render a virtual
|
||||||
* "RRO" tag on it, and it starts PRIVATE rather than inheriting the source's
|
* "RRO" tag on it, `IsDeveloperOwned`/`CloningAllowed` are cleared, its `Stats` start
|
||||||
* visibility. Returns the new room, or null when the source isn't in D1 or disallows
|
* at zero, and it starts PRIVATE rather than inheriting the source's visibility. Returns the new room, or null when the source isn't in D1 or disallows
|
||||||
* cloning.
|
* cloning.
|
||||||
*/
|
*/
|
||||||
export async function cloneRoom(
|
export async function cloneRoom(
|
||||||
@@ -702,15 +702,20 @@ export async function cloneRoom(
|
|||||||
// A user clone is not a Rec Room Original — clear the inherited flag, or the
|
// A user clone is not a Rec Room Original — clear the inherited flag, or the
|
||||||
// client renders a virtual "RRO" tag on the clone.
|
// client renders a virtual "RRO" tag on the clone.
|
||||||
IsRRO: false,
|
IsRRO: false,
|
||||||
|
// Neither is the cloner a developer, and a copy doesn't inherit the source's consent
|
||||||
|
// to be copied: the new owner opts their room into cloning themselves.
|
||||||
|
IsDeveloperOwned: false,
|
||||||
|
CloningAllowed: false,
|
||||||
// A brand-new room is unpublished: the owner publishes it by setting the room's
|
// A brand-new room is unpublished: the owner publishes it by setting the room's
|
||||||
// accessibility. Inheriting the source's would put the clone straight into the
|
// accessibility. Inheriting the source's would put the clone straight into the
|
||||||
// public feeds (hot/search/recommendations/similar all key on Accessibility === 1)
|
// public feeds (hot/search/recommendations/similar all key on Accessibility === 1)
|
||||||
// the moment it was made — every clone of a PUBLIC source, template or player room.
|
// the moment it was made — every clone of a PUBLIC source, template or player room.
|
||||||
Accessibility: Accessibility.Private,
|
Accessibility: Accessibility.Private,
|
||||||
Roles: roles,
|
Roles: roles,
|
||||||
// A fresh room has no engagement of its own — don't inherit the source's counters
|
// A fresh room has no engagement of its own — don't inherit any of the source's
|
||||||
// (the derived ones are recomputed per read, but the clone is returned as-is here).
|
// counters. `storedStats` alone would keep the blob-owned `VisitorCount`, which
|
||||||
Stats: storedStats(source.Stats),
|
// `hotScore` ranks on.
|
||||||
|
Stats: { ...ZERO_STATS },
|
||||||
CreatedAt: new Date().toISOString(),
|
CreatedAt: new Date().toISOString(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user