From 00843603f77de344c1d1e099bf4a0aacb06a6ac0 Mon Sep 17 00:00:00 2001 From: cpsiaki Date: Fri, 23 Jan 2026 01:24:03 -0600 Subject: [PATCH] Fix sceneDropId handling to prevent matching unintended assets All controllers now use the resolved sceneDropId from getDroppedAssetDataObject instead of directly using credentials.sceneDropId which could be empty. - getDroppedAssetDataObject now validates and returns the resolved sceneDropId - handleResetBoard uses resolved sceneDropId for queries and generateBoard calls - handleDropPiece uses resolved sceneDropId for asset queries - handlePlayerSelection uses resolved sceneDropId for asset queries This prevents queries with empty sceneDropId from matching all assets in a world. Co-Authored-By: Claude Opus 4.5 --- src/controllers/handleDropPiece.ts | 5 +++-- src/controllers/handlePlayerSelection.ts | 6 ++++-- src/controllers/handleResetBoard.ts | 11 +++++++---- src/utils/droppedAssets/getDroppedAssetDataObject.ts | 10 +++++++++- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/controllers/handleDropPiece.ts b/src/controllers/handleDropPiece.ts index b4ade97..92455ba 100644 --- a/src/controllers/handleDropPiece.ts +++ b/src/controllers/handleDropPiece.ts @@ -17,7 +17,7 @@ import { DroppedAssetInterface } from "@rtsdk/topia"; export const handleDropPiece = async (req: Request, res: Response) => { try { const credentials = req.credentials; - const { displayName, identityId, sceneDropId, urlSlug, visitorId } = credentials; + const { displayName, identityId, urlSlug, visitorId } = credentials; const { username } = req.body; let text = "", @@ -27,7 +27,8 @@ export const handleDropPiece = async (req: Request, res: Response) => { const column = parseInt(req.params.column); if (isNaN(column)) throw "Column id is required."; - const { keyAsset } = await getDroppedAssetDataObject(credentials, false); + // Get the resolved sceneDropId from getDroppedAssetDataObject + const { keyAsset, sceneDropId } = await getDroppedAssetDataObject(credentials, false); let { columns, diff --git a/src/controllers/handlePlayerSelection.ts b/src/controllers/handlePlayerSelection.ts index 1afaded..916a0be 100644 --- a/src/controllers/handlePlayerSelection.ts +++ b/src/controllers/handlePlayerSelection.ts @@ -8,13 +8,15 @@ export const handlePlayerSelection = async (req: Request, res: Response) => { const playerId = req.params.player; const isPlayer2 = parseInt(playerId) === 2; const credentials = req.credentials; - const { profileId, sceneDropId, urlSlug, visitorId } = credentials; + const { profileId, urlSlug, visitorId } = credentials; const { username } = req.body; let text = "", shouldUpdateGame = true; - const { keyAsset } = await getDroppedAssetDataObject(credentials, false); + // Get the resolved sceneDropId from getDroppedAssetDataObject + // This ensures we use a valid sceneDropId even if credentials.sceneDropId is empty + const { keyAsset, sceneDropId } = await getDroppedAssetDataObject(credentials, false); const { keyAssetId, playerCount, player1, player2 } = keyAsset.dataObject as GameDataType; try { diff --git a/src/controllers/handleResetBoard.ts b/src/controllers/handleResetBoard.ts index 1b11856..34e9f6e 100644 --- a/src/controllers/handleResetBoard.ts +++ b/src/controllers/handleResetBoard.ts @@ -14,16 +14,18 @@ import { DroppedAssetInterface, VisitorInterface } from "@rtsdk/topia"; export const handleResetBoard = async (req: Request, res: Response) => { try { const credentials = req.credentials; - const { assetId, sceneDropId, urlSlug, visitorId } = credentials; + const { assetId, urlSlug, visitorId } = credentials; const visitor: VisitorInterface = await Visitor.get(visitorId, urlSlug, { credentials }); const isAdmin = visitor.isAdmin; - const { keyAsset, wasDataObjectInitialized } = await getDroppedAssetDataObject(credentials, false); + // Get the resolved sceneDropId from getDroppedAssetDataObject + // This ensures we use a valid sceneDropId even if credentials.sceneDropId is empty + const { keyAsset, wasDataObjectInitialized, sceneDropId } = await getDroppedAssetDataObject(credentials, false); const { lastInteraction, player1, player2, resetCount } = keyAsset.dataObject as GameDataType; if (wasDataObjectInitialized) { - await generateBoard(credentials); + await generateBoard({ ...credentials, sceneDropId }); return res.status(200).send({ message: "Game created successfully" }); } @@ -32,6 +34,7 @@ export const handleResetBoard = async (req: Request, res: Response) => { const world = World.create(urlSlug, { credentials }); // get all assets with matching sceneDropId for full board rebuild + // Using the resolved sceneDropId from getDroppedAssetDataObject, not from credentials droppedAssets = await world.fetchDroppedAssetsBySceneDropId({ sceneDropId }); droppedAssets = droppedAssets.filter((item) => item.uniqueName !== "reset"); @@ -103,7 +106,7 @@ export const handleResetBoard = async (req: Request, res: Response) => { ), ); - if (isAdmin) promises.push(generateBoard(credentials)); + if (isAdmin) promises.push(generateBoard({ ...credentials, sceneDropId })); await Promise.all(promises); diff --git a/src/utils/droppedAssets/getDroppedAssetDataObject.ts b/src/utils/droppedAssets/getDroppedAssetDataObject.ts index 8f42aca..c49381f 100644 --- a/src/utils/droppedAssets/getDroppedAssetDataObject.ts +++ b/src/utils/droppedAssets/getDroppedAssetDataObject.ts @@ -23,8 +23,15 @@ export const getDroppedAssetDataObject = async (credentials: Credentials, isKeyA } } + // If sceneDropId is not provided, use keyAssetId as the sceneDropId + // This ensures we always have a valid sceneDropId for asset queries if (!sceneDropId) sceneDropId = keyAssetId; + // Validate that we have a valid sceneDropId before proceeding + if (!sceneDropId || sceneDropId.trim() === "") { + throw "Unable to determine a valid sceneDropId. This is required to safely query assets."; + } + // store keyAssetId by sceneDropId in World data object so that it can be accessed by any clickable asset // this supports a scene being dropped with the board already created instead of just the Reset button if (!dataObject || Object.keys(dataObject).length === 0) { @@ -41,7 +48,8 @@ export const getDroppedAssetDataObject = async (credentials: Credentials, isKeyA const wasDataObjectInitialized = await initializeDroppedAssetDataObject(keyAsset, sceneDropId); - return { keyAsset, wasDataObjectInitialized }; + // Return the resolved sceneDropId so callers can use it for subsequent queries + return { keyAsset, wasDataObjectInitialized, sceneDropId }; } catch (error) { return errorHandler({ error,