Skip to content

feat(variables): multiplayer variables through sockets, persist server side#933

Merged
icecrasher321 merged 3 commits intostagingfrom
fix/variables
Aug 11, 2025
Merged

feat(variables): multiplayer variables through sockets, persist server side#933
icecrasher321 merged 3 commits intostagingfrom
fix/variables

Conversation

@waleedlatif1
Copy link
Collaborator

Summary

multiplayer variables through sockets, persist server side

Type of Change

  • Bug fix

Testing

Tested manually.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

Screen.Recording.2025-08-10.at.8.44.00.PM.mov

@vercel
Copy link

vercel bot commented Aug 11, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
sim ✅ Ready (Inspect) Visit Preview 💬 Add feedback Aug 11, 2025 4:08am
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
docs ⬜️ Skipped (Inspect) Aug 11, 2025 4:08am

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greptile Summary

This PR implements a comprehensive multiplayer variables system for the workflow application, enabling real-time collaborative editing of workflow variables across multiple users. The changes transform the variables system from a local-only state management approach to a fully collaborative, server-side persisted architecture.

The key architectural changes include:

  1. Socket-based Real-time Synchronization: Added WebSocket handlers (setupVariablesHandlers) to manage variable operations (add, update, delete, duplicate) with immediate broadcasting to all connected users in the same workflow room.

  2. Server-side Persistence: Implemented database operations in handleVariableOperationTx that directly manipulate the workflow.variables JSON field, ensuring all variable changes are persisted before being broadcast to other clients.

  3. Collaborative Operation Queue: Extended the existing operation queue system with variable-specific debouncing (25ms timeout), cleanup mechanisms, and cancellation capabilities to handle rapid updates efficiently.

  4. Unified Variable Management: Modified the variables store to support predetermined IDs (providedId parameters) for consistent state across clients while removing local persistence middleware in favor of socket-based synchronization.

  5. Component Integration: Updated the Variables React component to use collaborative functions (collaborativeAddVariable, collaborativeUpdateVariable, etc.) instead of direct store mutations, ensuring all user interactions trigger socket events.

The implementation follows the established patterns used for collaborative blocks and subblocks, maintaining consistency with the existing multiplayer architecture. Variables are now loaded during workflow fetching and synchronized through the same socket infrastructure used for other workflow elements, providing a seamless collaborative experience where multiple users can create, edit, and manage variables simultaneously with instant visual feedback.

Confidence score: 3/5

  • This PR implements complex multiplayer functionality with potential race conditions and data consistency issues
  • Score reflects concerns about type safety in validation schemas, JSON field manipulation, and transaction handling
  • Pay close attention to socket-server/handlers/variables.ts and socket-server/database/operations.ts for data integrity validation

12 files reviewed, 11 comments

Edit Code Review Bot Settings | Greptile

return // Early return for position updates
}

if (target === 'variable' && ['add', 'remove', 'duplicate'].includes(operation)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider using a more maintainable approach like a Set or constant array for operation types to make future additions easier

Comment on lines +89 to +90
type: z.any(),
value: z.any(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Using z.any() for type and value reduces type safety. Consider defining specific schemas for variable types.

Context Used: Context - Avoid using type assertions to 'any' in TypeScript. Instead, ensure proper type definitions are used to maintain type safety. (link)

Comment on lines +149 to +156
if (variables && typeof variables === 'object') {
useVariablesStore.setState((state) => ({
variables: {
...state.variables,
...variables,
},
}))
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Potential variable conflict - this spreads all variables from DB into the store without checking workflowId. Variables from different workflows could overwrite each other if they have the same ID.

Comment on lines +105 to +114
z.object({
operation: z.literal('duplicate'),
target: z.literal('variable'),
payload: z.object({
sourceVariableId: z.string(),
id: z.string(),
}),
timestamp: z.number(),
operationId: z.string().optional(),
}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Duplicate operation lacks name, type, value, and workflowId fields that may be needed for complete variable duplication.

userName: session.userName,
metadata: {
workflowId,
operationId: crypto.randomUUID(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: The operationId from the original client request should be used instead of generating a new UUID for proper operation tracking

throw new Error(`Workflow ${workflowId} not found`)
}

const currentVariables = (workflowData[0].variables as Record<string, any>) || {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider using a more specific type instead of Record<string, any> to improve type safety

Context Used: Context - Avoid using type assertions to 'any' in TypeScript. Instead, ensure proper type definitions are used to maintain type safety. (link)

Comment on lines +516 to +521
const operationsToCancel = state.operations.filter(
(op) =>
(op.operation.target === 'variable' && op.operation.payload?.variableId === variableId) ||
(op.operation.target === 'variable' &&
op.operation.payload?.sourceVariableId === variableId)
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: The filter logic uses both variableId and sourceVariableId but only variableId is used in the debounce key generation above. Ensure consistency in how variable operations are identified.

const collaborativeAddVariable = useCallback(
(variableData: { name: string; type: any; value: any; workflowId: string }) => {
const id = crypto.randomUUID()
variablesStore.addVariable(variableData, id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Local variable addition before queuing operation could create inconsistencies if the queue operation fails

return
}

const variables = (workflowRecord.variables as any) || {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Using any type assertion loses type safety. Consider defining proper types for the variables structure.

Context Used: Context - Avoid using type assertions to 'any' in TypeScript. Instead, ensure proper type definitions are used to maintain type safety. (link)

Comment on lines +15 to +16
const roomManager =
deps instanceof Object && 'roomManager' in deps ? deps.roomManager : (deps as RoomManager)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Type guard logic is overly complex. Consider extracting this pattern into a utility function since it's repeated across handlers.

get().processNextOperation()
},

cancelOperationsForVariable: (variableId: string) => {
Copy link
Collaborator

@icecrasher321 icecrasher321 Aug 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this do? Is this for debouncing?

@icecrasher321 icecrasher321 merged commit bc455d5 into staging Aug 11, 2025
5 checks passed
@waleedlatif1 waleedlatif1 deleted the fix/variables branch August 15, 2025 17:03
arenadeveloper02 pushed a commit to arenadeveloper02/p2-sim that referenced this pull request Sep 19, 2025
…r side (simstudioai#933)

* feat(variables): multiplayer variables through sockets, persist server side

* remove extraneous comments

* breakout variables handler in sockets
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants