Skip to content

Conversation

@Scriptwonder
Copy link
Collaborator

@Scriptwonder Scriptwonder commented Jan 26, 2026

Description

Optimize token usage for VFX component.

Type of Change

Save your change type

  • Refactoring (no functional changes)

Changes Made

Shorten the description and cut the token usage by half, also manage to sideload a lot of parameter assignment to C# side.

Testing/Screenshots/Recordings

Before Change:
截屏2026-01-25 上午2 02 04
After Change:
截屏2026-01-25 下午7 10 30

Documentation Updates

  • I have added/removed/modified tools or resources
  • If yes, I have updated all documentation files using:
    • The LLM prompt at tools/UPDATE_DOCS_PROMPT.md (recommended)
    • Manual updates following the guide at tools/UPDATE_DOCS.md

Related Issues

Additional Notes

Summary by Sourcery

Simplify the manage_vfx tool interface by moving most action-specific parameters into a generic properties payload handled on the C# side, while improving defaults and documentation for VFX components.

Enhancements:

  • Shorten and generalize the MCP manage_vfx tool description and parameters, replacing many explicit arguments with a single properties field mapped to ManageVFX.cs.
  • Normalize and alias VFX parameter handling in ManageVFX.cs so both top-level and nested properties are merged, camel-cased, and prepared consistently for downstream handlers.
  • Introduce automatic default material assignment for ParticleSystem, LineRenderer, and TrailRenderer based on the active render pipeline, with caching and helper utilities.
  • Enhance particle, line, and trail writer/control tools to ensure materials are set before operations and to support additional convenience options such as inline width, color, time, and position updates.
  • Relax renderer material setting to optionally auto-assign default materials when materialPath is omitted, improving robustness of VFX commands.
  • Add CLI-side parameter normalization for vfx commands so non-core arguments are grouped under properties before calling manage_vfx.
  • Slightly harden ManageScript action parsing and streamline manage_texture tool description text.

Documentation:

  • Expand and reorganize inline XML documentation for ManageVFX to clearly describe component requirements, targeting, automatic material assignment, actions, and common parameters.

Summary by CodeRabbit

  • New Features

    • Automatic default material assignment for ParticleSystem, LineRenderer, and TrailRenderer.
    • Flexible VFX command parameters with aliasing and normalization.
    • Line renderer styling: width (start/end), color, positions, and material selection.
  • Bug Fixes

    • Ensures VFX renderers always have valid materials to avoid missing-material issues.
  • Refactor

    • VFX command surface consolidated to use a single properties payload with normalized routing.

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings January 26, 2026 00:12
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 26, 2026

Reviewer's Guide

Refactors the manage_vfx interface to drastically reduce exposed parameters by moving most per-action arguments into a generic properties payload handled on the C# side, adds robust parameter normalization and aliases, introduces auto-assigned default materials for VFX-related renderers across pipelines, and updates CLI commands and VFX helper code accordingly.

Sequence diagram for manage_vfx request flow with normalized properties

sequenceDiagram
    actor User
    participant CLI_vfx as cli_vfx_command
    participant PyTool as manage_vfx_tool_py
    participant Unity as Unity_MCP_connection
    participant ManageVFX as ManageVFX_cs
    participant VfxHandler as VFX_or_Particle_Line_Trail_handler
    participant RendererHelpers as RendererHelpers
    participant RenderPipelineUtil as RenderPipelineUtility

    User->>CLI_vfx: run vfx subcommand
    CLI_vfx->>CLI_vfx: build params dict
    CLI_vfx->>CLI_vfx: _normalize_vfx_params
    CLI_vfx->>PyTool: run_command manage_vfx(normalized params)

    PyTool->>PyTool: normalize_action
    PyTool->>PyTool: params_dict = {action, target, searchMethod}
    PyTool->>PyTool: attach properties payload
    PyTool->>Unity: send manage_vfx request

    Unity->>ManageVFX: HandleCommand(JObject params)
    ManageVFX->>ManageVFX: NormalizeParams
    ManageVFX->>ManageVFX: ExtractProperties(properties)
    ManageVFX->>ManageVFX: NormalizeKey and NormalizeToken

    ManageVFX->>VfxHandler: HandleParticleSystemAction or HandleVFXGraphAction or HandleLineRendererAction or HandleTrailRendererAction

    VfxHandler->>RendererHelpers: EnsureMaterial(renderer)
    RendererHelpers->>RenderPipelineUtil: GetOrCreateDefaultVFXMaterial(VFXComponentType)
    RenderPipelineUtil-->>RendererHelpers: default Material
    RendererHelpers-->>VfxHandler: renderer.sharedMaterial assigned

    VfxHandler-->>ManageVFX: result payload
    ManageVFX-->>Unity: result
    Unity-->>PyTool: result
    PyTool-->>CLI_vfx: result
    CLI_vfx-->>User: formatted output
Loading

Class diagram for updated VFX management and helpers

classDiagram
    class ManageVFX {
        +static Dictionary~string,string~ ParamAliases
        +static object HandleCommand(JObject params)
        -static JObject NormalizeParams(JObject source)
        -static JObject ExtractProperties(JObject source)
        -static string NormalizeKey(string key, bool allowAliases)
        -static JToken NormalizeToken(JToken token)
        -static string ToCamelCase(string key)
    }

    class RendererHelpers {
        +static void EnsureMaterial(Renderer renderer)
        +static void ApplyCommonRendererProperties(JObject params, List~string~ changes, Action~bool~ setReceiveShadows, Action~UnityEngine.Rendering.ShadowCastingMode~ setShadowCastingMode, Action~UnityEngine.Rendering.LightProbeUsage~ setLightProbeUsage, Action~UnityEngine.Rendering.ReflectionProbeUsage~ setReflectionProbeUsage, Action~UnityEngine.Rendering.MotionVectorGenerationMode~ setMotionVectorGenerationMode, Action~int~ setSortingLayerID, Action~string~ setSortingLayerName, Action~int~ setSortingOrder, Action~int~ setRenderingLayerMask)
        +static void ApplyColorProperties(JObject params, List~string~ changes, Action~Color~ setColor, Action~Gradient~ setGradient, Action~Color~ setStartColor, Action~Color~ setEndColor)
        +static object SetRendererMaterial(Renderer renderer, JObject params, string undoName, Func~string,Material~ findMaterial, bool autoAssignDefault)
    }

    class RenderPipelineUtility {
        <<static>>
        enum PipelineKind
        enum VFXComponentType
        +static PipelineKind GetActivePipeline()
        +static Material GetOrCreateDefaultVFXMaterial(VFXComponentType componentType)
        -static Shader ResolveDefaultUnlitShader(PipelineKind pipeline)
        -static void WarnIfPipelineMismatch(string shaderName, PipelineKind active, PipelineKind expected)
    }

    class LineCreate {
        +static object CreateLine(JObject params)
        +static object CreateCircle(JObject params)
        +static object CreateArc(JObject params)
        +static object CreateBezier(JObject params)
    }

    class LineWrite {
        +static object SetPositions(JObject params)
        +static object AddPosition(JObject params)
        +static object SetPosition(JObject params)
        +static object SetWidth(JObject params)
        +static object SetColor(JObject params)
        +static object SetProperties(JObject params)
    }

    class TrailWrite {
        +static object SetTime(JObject params)
        +static object SetWidth(JObject params)
        +static object SetColor(JObject params)
        +static object SetProperties(JObject params)
    }

    class ParticleWrite {
        +static object SetMain(JObject params)
        +static object SetEmission(JObject params)
        +static object SetShape(JObject params)
        +static object SetColorOverLifetime(JObject params)
        +static object SetSizeOverLifetime(JObject params)
        +static object SetVelocityOverLifetime(JObject params)
        +static object SetNoise(JObject params)
        +static object SetRenderer(JObject params)
    }

    class ParticleControl {
        +static object Control(JObject params, string action)
        +static object AddBurst(JObject params)
    }

    class TrailControl {
        +static object Emit(JObject params)
    }

    ManageVFX ..> RendererHelpers : uses EnsureMaterial via handlers
    ManageVFX ..> RenderPipelineUtility : uses through RendererHelpers

    LineCreate ..> RendererHelpers : EnsureMaterial
    LineWrite ..> RendererHelpers : EnsureMaterial
    TrailWrite ..> RendererHelpers : EnsureMaterial
    ParticleWrite ..> RendererHelpers : EnsureMaterial
    ParticleControl ..> RendererHelpers : EnsureMaterial
    TrailControl ..> RendererHelpers : EnsureMaterial

    RendererHelpers ..> RenderPipelineUtility : GetOrCreateDefaultVFXMaterial

    RenderPipelineUtility o-- RenderPipelineUtility.VFXComponentType
    RenderPipelineUtility o-- RenderPipelineUtility.PipelineKind
Loading

Flow diagram for VFX parameter normalization and material auto-assignment

flowchart TD
    A[CLI builds params dict] --> B[_normalize_vfx_params]
    B -->|Move non top-level keys| C[properties dict]
    B --> D[params with action,target,searchMethod,properties]
    C --> D
    D --> E[manage_vfx Python tool]
    E -->|wraps properties if provided| F[Unity ManageVFX.HandleCommand]

    F --> G[NormalizeParams]
    G --> H[ExtractProperties from properties key]
    H --> I[NormalizeKey and NormalizeToken]
    I --> J[normalizedParams JObject]

    J --> K{action prefix}
    K -->|particle_*| L[HandleParticleSystemAction]
    K -->|vfx_*| M[HandleVFXGraphAction]
    K -->|line_*| N[HandleLineRendererAction]
    K -->|trail_*| O[HandleTrailRendererAction]

    L --> P[ParticleWrite or ParticleControl]
    N --> Q[LineCreate or LineWrite]
    O --> R[TrailWrite or TrailControl]

    P --> S[RendererHelpers.EnsureMaterial]
    Q --> S
    R --> S

    S --> T[RenderPipelineUtility.GetOrCreateDefaultVFXMaterial]
    T --> U[default Material cached per pipeline and VFXComponentType]
    U --> V[renderer.sharedMaterial assigned]

    V --> W[Apply action-specific changes]
    W --> X[result success,message]
    X --> Y[return to CLI and user]
Loading

File-Level Changes

Change Details Files
Slim down the Python manage_vfx tool interface and move per-action arguments into a generic properties payload.
  • Shorten the tool description to a compact summary of components and prefixes, directing all action-specific inputs into a properties field that matches ManageVFX.cs.
  • Collapse the enormous parameter list on manage_vfx into a minimal signature with action, target, search_method, and properties (dict or JSON string).
  • Build the params_dict using action, optional properties, target, and searchMethod only, then strip None values before sending to Unity.
Server/src/services/tools/manage_vfx.py
Introduce parameter normalization and alias handling in ManageVFX to consume the new properties payload and accept flexible key formats.
  • Add ParamAliases and NormalizeParams/NormalizeKey/NormalizeToken/ToCamelCase helpers to merge top-level params with nested properties, normalize snake_case to camelCase, and map known aliases.
  • Update HandleCommand to normalize params once, then route normalizedParams into the particle, VFX, line, and trail handler methods instead of the raw JObject.
  • Document all available actions, component requirements, targeting, automatic material assignment, and common parameter patterns in the ManageVFX XML doc comment.
MCPForUnity/Editor/Tools/Vfx/ManageVFX.cs
Add cross-pipeline default material generation and auto-assignment helpers for VFX components.
  • Extend RenderPipelineUtility with a VFXComponentType enum, a cache of default VFX materials, and GetOrCreateDefaultVFXMaterial which chooses built-in defaults or creates unlit materials appropriate to the active render pipeline and component type.
  • Add RendererHelpers.EnsureMaterial to assign a default material to ParticleSystemRenderer, LineRenderer, and TrailRenderer instances when none is set.
  • Enhance RendererHelpers.SetRendererMaterial to optionally auto-assign a default material when materialPath is omitted, returning a descriptive message when this occurs.
MCPForUnity/Editor/Helpers/RenderPipelineUtility.cs
MCPForUnity/Editor/Helpers/RendererHelpers.cs
Ensure VFX operations automatically have materials and better behavior when configuring or playing effects.
  • In ParticleWrite, call EnsureMaterial on the ParticleSystemRenderer before mutating main/emission/shape/color/size/velocity/noise/renderer modules, and handle duration changes by stopping and restarting systems as needed while logging changes.
  • In ParticleControl, ensure a material is assigned before play/restart, and before adding bursts.
  • In TrailControl, ensure TrailRenderer has a material before emitting points.
  • In LineWrite and TrailWrite, ensure materials are present before setting positions, widths, colors, properties, and add support in SetProperties methods to set material, positions/positionCount (line) and time/width-related fields (trail) directly from params.
MCPForUnity/Editor/Tools/Vfx/ParticleWrite.cs
MCPForUnity/Editor/Tools/Vfx/ParticleControl.cs
MCPForUnity/Editor/Tools/Vfx/TrailControl.cs
MCPForUnity/Editor/Tools/Vfx/LineWrite.cs
MCPForUnity/Editor/Tools/Vfx/TrailWrite.cs
Improve line shape creation APIs so they auto-assign materials and honor optional width and color parameters.
  • Import RendererHelpers into LineCreate and call EnsureMaterial on created LineRenderer instances in CreateLine, CreateCircle, CreateArc, and CreateBezier.
  • Apply optional width/ startWidth/ endWidth and color/ startColor/ endColor parameters when creating shapes to fully configure the renderer on creation.
MCPForUnity/Editor/Tools/Vfx/LineCreate.cs
Normalize CLI vfx commands to use the new properties-based payload automatically.
  • Introduce _VFX_TOP_LEVEL_KEYS and _normalize_vfx_params in the vfx CLI module to move all non-core keys into a nested properties dict (merging with any existing properties), and drop None values.
  • Wrap all manage_vfx CLI invocations (particle, line, trail, and vfx_raw commands) with _normalize_vfx_params before calling run_command so existing flags are transparently mapped into the new structure.
Server/src/cli/commands/vfx.py
Minor robustness and docstring/tokens cleanup in unrelated tools.
  • Use ToLowerInvariant instead of ToLower when normalizing script actions in ManageScript.HandleCommand for culture-safe comparison.
  • Trim the manage_texture tool description to remove an extra sentence about CRUD/sprite creation for token savings without changing behavior.
MCPForUnity/Editor/Tools/ManageScript.cs
Server/src/services/tools/manage_texture.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 26, 2026

Caution

Review failed

The pull request is closed.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds pipeline-aware default VFX material creation/caching and automatic material assignment across Particle/Line/Trail handlers; introduces parameter-normalization for VFX commands and consolidates server manage_vfx to accept a single properties payload.

Changes

Cohort / File(s) Summary
Material Management Infrastructure
MCPForUnity/Editor/Helpers/RenderPipelineUtility.cs, MCPForUnity/Editor/Helpers/RendererHelpers.cs
Adds VFXComponentType enum, GetOrCreateDefaultVFXMaterial with pipeline-aware caching/creation, private material cache, and EnsureMaterial(Renderer) plus optional autoAssignDefault behavior in SetRendererMaterial.
Particle VFX Handlers
MCPForUnity/Editor/Tools/Vfx/ParticleControl.cs, MCPForUnity/Editor/Tools/Vfx/ParticleWrite.cs
Calls EnsureMaterial before play/restart/burst and at start of multiple Set* methods; stops/restarts PS when duration changes; records/warns on material changes.
Line VFX Handlers
MCPForUnity/Editor/Tools/Vfx/LineCreate.cs, MCPForUnity/Editor/Tools/Vfx/LineWrite.cs
Ensures material in create/write paths; adds width and color parameters (width/startWidth/endWidth, color/startColor/endColor); SetProperties supports materialPath, position array, and positionCount.
Trail VFX Handlers
MCPForUnity/Editor/Tools/Vfx/TrailControl.cs, MCPForUnity/Editor/Tools/Vfx/TrailWrite.cs
Ensures material in Emit and setters; extends SetProperties to accept materialPath, time, and width variants; records changes and warns on missing materials.
VFX Command Management (Editor)
MCPForUnity/Editor/Tools/Vfx/ManageVFX.cs, MCPForUnity/Editor/Tools/ManageScript.cs
Adds ParamAliases, NormalizeParams, ExtractProperties, NormalizeKey/Token, ToCamelCase; routes actions using normalized params; adds ping action; uses ToLowerInvariant() for action parsing.
CLI / Server
Server/src/cli/commands/vfx.py, Server/src/services/tools/manage_vfx.py, Server/src/services/tools/manage_texture.py
CLI adds _normalize_vfx_params to fold non-top-level keys into properties; server manage_vfx signature simplified to accept properties dict/string; minor description text edit in manage_texture.

Sequence Diagram(s)

sequenceDiagram
    participant User as CLI User
    participant CLI as Server CLI (commands/vfx.py)
    participant Server as Server/manage_vfx
    participant Unity as Unity Editor (ManageVFX)
    participant RenderUtil as RenderPipelineUtility / RendererHelpers

    User->>CLI: send VFX command
    CLI->>Server: run_command("manage_vfx", _normalize_vfx_params(params))
    Server->>Unity: run_command("manage_vfx", {action, target, properties})
    Unity->>Unity: NormalizeParams -> determine action and target
    Unity->>RenderUtil: EnsureMaterial / GetOrCreateDefaultVFXMaterial (if renderer missing material)
    RenderUtil-->>Unity: return Material (cached or created)
    Unity-->>Server: return result (success/warnings/errors)
    Server-->>CLI: forward response
    CLI-->>User: display result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • justinpbarnett

Poem

🐰 I hopped through shaders, cached each hue,
Assigned a default when a renderer was blue.
Params tidied up, keys neat and new,
Lines, Trails, Particles — ready to view.
A rabbit’s nod: all systems snug and true.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main objective: optimizing token usage for the VFX component through refactoring and parameter consolidation.
Description check ✅ Passed The description covers all required template sections including type of change, changes made, testing evidence, and documentation updates, meeting the repository's standards.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The new NormalizeParams in ManageVFX.cs merges properties into the top-level params with top-level keys taking precedence; if that’s intentional, consider adding a brief comment, otherwise you may want to invert the precedence or explicitly resolve collisions to avoid surprising overrides.
  • In vfx.py, _normalize_vfx_params has its own hard-coded set of top-level keys; to avoid drift with the C# side (e.g., if more top-level keys are added later), consider centralizing or deriving this list from a single source or at least adding a TODO to keep it aligned with ManageVFX.NormalizeParams.
  • The param alias map in ManageVFX.ParamAliases currently only covers a handful of legacy keys; if you expect more existing callers to rely on old parameter names, it may be worth expanding this map or documenting the breaking changes so consumers know which keys are still auto-normalized versus which now must go through properties.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new `NormalizeParams` in `ManageVFX.cs` merges `properties` into the top-level params with top-level keys taking precedence; if that’s intentional, consider adding a brief comment, otherwise you may want to invert the precedence or explicitly resolve collisions to avoid surprising overrides.
- In `vfx.py`, `_normalize_vfx_params` has its own hard-coded set of top-level keys; to avoid drift with the C# side (e.g., if more top-level keys are added later), consider centralizing or deriving this list from a single source or at least adding a TODO to keep it aligned with `ManageVFX.NormalizeParams`.
- The param alias map in `ManageVFX.ParamAliases` currently only covers a handful of legacy keys; if you expect more existing callers to rely on old parameter names, it may be worth expanding this map or documenting the breaking changes so consumers know which keys are still auto-normalized versus which now must go through `properties`.

## Individual Comments

### Comment 1
<location> `Server/src/cli/commands/vfx.py:26-27` </location>
<code_context>
+_VFX_TOP_LEVEL_KEYS = {"action", "target", "searchMethod", "properties"}
+
+
+def _normalize_vfx_params(params: dict[str, Any]) -> dict[str, Any]:
+    params = dict(params)
+    properties: dict[str, Any] = {}
+    for key in list(params.keys()):
+        if key in _VFX_TOP_LEVEL_KEYS:
+            continue
+        properties[key] = params.pop(key)
+
+    if properties:
+        existing = params.get("properties")
+        if isinstance(existing, dict):
+            params["properties"] = {**properties, **existing}
+        else:
</code_context>

<issue_to_address>
**suggestion (bug_risk):** CLI flag precedence vs JSON `properties` may be inverted in merge logic

In `_normalize_vfx_params`, when both top-level keys and an existing `properties` dict are present, `params["properties"] = {**properties, **existing}` causes existing `properties` values to override CLI flags for overlapping keys. If CLI flags are meant to take precedence over the JSON payload (which is typical), the merge order should be `{**existing, **properties}` instead. Please confirm and adjust to match the intended precedence before this becomes relied-on behavior.

```suggestion
        if isinstance(existing, dict):
            # Existing JSON `properties` are the base; CLI/top-level flags take precedence.
            params["properties"] = {**existing, **properties}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +26 to +27
if isinstance(existing, dict):
params["properties"] = {**properties, **existing}
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): CLI flag precedence vs JSON properties may be inverted in merge logic

In _normalize_vfx_params, when both top-level keys and an existing properties dict are present, params["properties"] = {**properties, **existing} causes existing properties values to override CLI flags for overlapping keys. If CLI flags are meant to take precedence over the JSON payload (which is typical), the merge order should be {**existing, **properties} instead. Please confirm and adjust to match the intended precedence before this becomes relied-on behavior.

Suggested change
if isinstance(existing, dict):
params["properties"] = {**properties, **existing}
if isinstance(existing, dict):
# Existing JSON `properties` are the base; CLI/top-level flags take precedence.
params["properties"] = {**existing, **properties}

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Refactors the VFX tool interface to reduce token usage by moving most action-specific parameters into a single properties payload, and shifts more parameter normalization/material handling to the Unity (C#) side.

Changes:

  • Simplified manage_vfx tool schema to {action, target, searchMethod, properties} and updated CLI to pack extra args into properties.
  • Added Unity-side parameter normalization (snake_case → camelCase, aliasing) and expanded VFX tool documentation/comments in ManageVFX.cs.
  • Introduced automatic default material assignment for Particle/Line/Trail renderers, plus additional property handling in several write/control paths.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
Server/src/services/tools/manage_vfx.py Collapses many parameters into a single properties payload for reduced token usage.
Server/src/services/tools/manage_texture.py Minor description string formatting tweak.
Server/src/cli/commands/vfx.py Adds _normalize_vfx_params and routes CLI requests through properties.
MCPForUnity/Editor/Tools/Vfx/TrailWrite.cs Ensures materials and expands trail_set_properties handling (material/time/width).
MCPForUnity/Editor/Tools/Vfx/TrailControl.cs Ensures material before trail_emit.
MCPForUnity/Editor/Tools/Vfx/ParticleWrite.cs Ensures material in multiple particle write actions; improves renderer material logging and duration-change behavior.
MCPForUnity/Editor/Tools/Vfx/ParticleControl.cs Ensures material before particle play/restart and burst operations.
MCPForUnity/Editor/Tools/Vfx/ManageVFX.cs Adds parameter normalization + aliases and expands inline documentation.
MCPForUnity/Editor/Tools/Vfx/LineWrite.cs Ensures material and expands line_set_properties handling (material/positions/positionCount).
MCPForUnity/Editor/Tools/Vfx/LineCreate.cs Ensures material and applies optional width/color for created shapes.
MCPForUnity/Editor/Tools/ManageScript.cs Uses ToLowerInvariant() for action normalization.
MCPForUnity/Editor/Helpers/RendererHelpers.cs Adds EnsureMaterial and extends SetRendererMaterial to support default assignment.
MCPForUnity/Editor/Helpers/RenderPipelineUtility.cs Adds default VFX material creation/caching per render pipeline + component type.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

{
Material defaultMat = RenderPipelineUtility.GetOrCreateDefaultVFXMaterial(componentType.Value);
if (defaultMat != null)
{
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

EnsureMaterial mutates renderer.sharedMaterial without recording an Undo step or marking the object dirty. Since many call sites invoke EnsureMaterial(...) before Undo.RecordObject(...), the auto-assigned material change won’t be undoable and may not persist reliably in editor workflows. Consider either (a) moving EnsureMaterial calls to after Undo.RecordObject, or (b) updating EnsureMaterial to handle Undo/SetDirty (e.g., accept an optional undo label and call Undo.RecordObject / EditorUtility.SetDirty when it assigns a material).

Suggested change
{
{
Undo.RecordObject(renderer, "Assign default VFX material");
EditorUtility.SetDirty(renderer);

Copilot uses AI. Check for mistakes.
Comment on lines 189 to 191
catch (JsonException)
{
return null;
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

ExtractProperties silently swallows JsonException when properties is provided as a JSON string. This makes invalid JSON inputs fail “quietly” (properties are ignored) and can be very hard to debug from the caller side. Consider surfacing a clear error (e.g., throw with a descriptive message so HandleCommand returns it), or at least logging a warning that properties JSON could not be parsed.

Suggested change
catch (JsonException)
{
return null;
catch (JsonException ex)
{
throw new JsonException(
$"Failed to parse 'properties' JSON string. Raw value: {token}",
ex);

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +31
def _normalize_vfx_params(params: dict[str, Any]) -> dict[str, Any]:
params = dict(params)
properties: dict[str, Any] = {}
for key in list(params.keys()):
if key in _VFX_TOP_LEVEL_KEYS:
continue
properties[key] = params.pop(key)

if properties:
existing = params.get("properties")
if isinstance(existing, dict):
params["properties"] = {**properties, **existing}
else:
params["properties"] = properties

return {k: v for k, v in params.items() if v is not None}
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

_normalize_vfx_params is new behavior that changes the wire format (moves non-top-level keys into properties). Existing CLI tests exercise VFX commands, but none assert the request payload shape, so regressions here could go unnoticed. Add targeted unit tests that verify keys like time, positions, etc. end up under properties while action/target/searchMethod stay top-level, including the merge behavior when properties is already supplied.

Copilot uses AI. Check for mistakes.
EditorUtility.SetDirty(ps);

// Restart particle system if it was playing
if (needsStop && wasPlaying)
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

Condition is always true because of access to local variable needsStop.

Suggested change
if (needsStop && wasPlaying)
if (needsStop)

Copilot uses AI. Check for mistakes.
TrailRenderer
}

private static Dictionary<string, Material> s_DefaultVFXMaterials = new Dictionary<string, Material>();
Copy link

Copilot AI Jan 26, 2026

Choose a reason for hiding this comment

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

Field 's_DefaultVFXMaterials' can be 'readonly'.

Suggested change
private static Dictionary<string, Material> s_DefaultVFXMaterials = new Dictionary<string, Material>();
private static readonly Dictionary<string, Material> s_DefaultVFXMaterials = new Dictionary<string, Material>();

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@MCPForUnity/Editor/Tools/Vfx/LineWrite.cs`:
- Around line 16-20: Move the positions validation before any renderer mutation:
check `@params`["positions"] into posArr and return early if null before calling
RendererHelpers.EnsureMaterial(lr); in the LineWrite handling (referencing
posArr, `@params`["positions"], RendererHelpers.EnsureMaterial and lr), so
EnsureMaterial is only invoked when positions are present.
- Around line 126-163: The code currently treats an empty JArray for
`@params`["positions"] as "do nothing" and also prevents the positionCount
fallback; update the positions handling in LineWrite.cs so that when
`@params`["positions"] is present you always handle it: cast to JArray (posArr)
and if posArr.Count > 0 parse and SetPositions as before, otherwise clear the
line by setting lr.positionCount = 0 (and/or lr.SetPositions(new Vector3[0]))
and record changes.Add("positions(0)"); also change the subsequent positionCount
check so it runs only when `@params`["positions"] is null (i.e., keep it as an
else branch based on presence of `@params`["positions"] rather than posArr.Count).
♻️ Duplicate comments (1)
MCPForUnity/Editor/Tools/Vfx/LineWrite.cs (1)

58-61: Duplicate: validate before mutating renderer state.

Same concern as above for invalid indices in SetPosition.

🧹 Nitpick comments (8)
Server/src/services/tools/manage_vfx.py (1)

42-42: Consider iterable unpacking for cleaner list construction.

Per static analysis (RUF005), iterable unpacking is preferred over concatenation for combining lists.

Suggested refactor
-ALL_ACTIONS = ["ping"] + PARTICLE_ACTIONS + VFX_ACTIONS + LINE_ACTIONS + TRAIL_ACTIONS
+ALL_ACTIONS = ["ping", *PARTICLE_ACTIONS, *VFX_ACTIONS, *LINE_ACTIONS, *TRAIL_ACTIONS]
MCPForUnity/Editor/Tools/Vfx/TrailWrite.cs (2)

100-112: Consider using RendererHelpers.ApplyWidthProperties instead of duplicating width logic.

The SetWidth method (lines 38-41) already uses RendererHelpers.ApplyWidthProperties, which also handles widthCurve and widthMultiplier. This inline implementation misses those parameters and duplicates logic.

♻️ Suggested refactor
-            // Handle width properties if provided
-            if (`@params`["width"] != null || `@params`["startWidth"] != null || `@params`["endWidth"] != null)
-            {
-                if (`@params`["width"] != null)
-                {
-                    float w = `@params`["width"].ToObject<float>();
-                    tr.startWidth = w;
-                    tr.endWidth = w;
-                    changes.Add("width");
-                }
-                if (`@params`["startWidth"] != null) { tr.startWidth = `@params`["startWidth"].ToObject<float>(); changes.Add("startWidth"); }
-                if (`@params`["endWidth"] != null) { tr.endWidth = `@params`["endWidth"].ToObject<float>(); changes.Add("endWidth"); }
-            }
+            // Handle width properties using shared helper
+            RendererHelpers.ApplyWidthProperties(`@params`, changes,
+                v => tr.startWidth = v, v => tr.endWidth = v,
+                v => tr.widthCurve = v, v => tr.widthMultiplier = v,
+                ManageVfxCommon.ParseAnimationCurve);

82-95: Material assignment doesn't record undo.

The material assignment at line 88 modifies tr.sharedMaterial but Undo.RecordObject was called at line 79 before this block. However, if only the material is changed (and no other properties), the undo will still capture it. This is fine, but consider adding Undo.RecordObject specifically for material changes for consistency with SetMaterial which uses RendererHelpers.SetRendererMaterial.

MCPForUnity/Editor/Tools/Vfx/ParticleWrite.cs (3)

53-58: Redundant condition check.

The condition needsStop && wasPlaying is redundant since needsStop is already defined as @params["duration"] != null && wasPlaying on line 26. If needsStop is true, wasPlaying is guaranteed to be true.

♻️ Simplify condition
-            if (needsStop && wasPlaying)
+            if (needsStop)

68-73: Repetitive material initialization pattern across multiple methods.

The same 5-line block for getting the renderer and ensuring material is repeated in SetEmission, SetShape, SetColorOverLifetime, SetSizeOverLifetime, SetVelocityOverLifetime, and SetNoise. Consider extracting to a helper method if this pattern continues to expand.

♻️ Example helper extraction
private static void EnsureParticleSystemMaterial(ParticleSystem ps)
{
    var renderer = ps.GetComponent<ParticleSystemRenderer>();
    if (renderer != null)
    {
        RendererHelpers.EnsureMaterial(renderer);
    }
}

Then each method would simply call:

EnsureParticleSystemMaterial(ps);

284-289: Missing warning log for trailMaterialPath not found.

For consistency with the materialPath handling above (lines 278-281), consider adding a warning when trailMaterialPath cannot be resolved.

♻️ Add warning for consistency
             if (`@params`["trailMaterialPath"] != null)
             {
-                var findInst = new JObject { ["find"] = `@params`["trailMaterialPath"].ToString() };
+                string trailMatPath = `@params`["trailMaterialPath"].ToString();
+                var findInst = new JObject { ["find"] = trailMatPath };
                 Material mat = ObjectResolver.Resolve(findInst, typeof(Material)) as Material;
-                if (mat != null) { renderer.trailMaterial = mat; changes.Add("trailMaterial"); }
+                if (mat != null)
+                {
+                    renderer.trailMaterial = mat;
+                    changes.Add($"trailMaterial={mat.name}");
+                }
+                else
+                {
+                    McpLog.Warn($"Trail material not found at path: {trailMatPath}. Keeping existing trail material.");
+                }
             }
MCPForUnity/Editor/Tools/Vfx/ManageVFX.cs (2)

183-193: Consider logging JSON parse failures.

When properties is a string that fails to parse as JSON, the error is silently swallowed. This could make debugging difficult for API consumers who accidentally pass malformed JSON.

♻️ Add warning log
             if (token.Type == JTokenType.String)
             {
                 try
                 {
                     return JToken.Parse(token.ToString()) as JObject;
                 }
                 catch (JsonException)
                 {
+                    McpLog.Warn($"Failed to parse 'properties' string as JSON: {token.ToString().Substring(0, Math.Min(100, token.ToString().Length))}...");
                     return null;
                 }
             }

313-316: Consider whether to expose stack traces in error responses.

Including stackTrace in the error response is helpful for debugging but may expose internal implementation details. Since this is a Unity Editor tool (not production runtime), this is likely acceptable, but worth considering for security-conscious deployments.

Comment on lines +16 to 20
RendererHelpers.EnsureMaterial(lr);

JArray posArr = @params["positions"] as JArray;
if (posArr == null) return new { success = false, message = "Positions array required" };

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid side effects when the positions array is missing.

EnsureMaterial runs before validating positions, so an invalid request still mutates the renderer. Consider validating first and only then mutating.

🔧 Proposed fix
-            RendererHelpers.EnsureMaterial(lr);
-
-            JArray posArr = `@params`["positions"] as JArray;
-            if (posArr == null) return new { success = false, message = "Positions array required" };
+            JArray posArr = `@params`["positions"] as JArray;
+            if (posArr == null) return new { success = false, message = "Positions array required" };
+
+            RendererHelpers.EnsureMaterial(lr);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
RendererHelpers.EnsureMaterial(lr);
JArray posArr = @params["positions"] as JArray;
if (posArr == null) return new { success = false, message = "Positions array required" };
JArray posArr = `@params`["positions"] as JArray;
if (posArr == null) return new { success = false, message = "Positions array required" };
RendererHelpers.EnsureMaterial(lr);
🤖 Prompt for AI Agents
In `@MCPForUnity/Editor/Tools/Vfx/LineWrite.cs` around lines 16 - 20, Move the
positions validation before any renderer mutation: check `@params`["positions"]
into posArr and return early if null before calling
RendererHelpers.EnsureMaterial(lr); in the LineWrite handling (referencing
posArr, `@params`["positions"], RendererHelpers.EnsureMaterial and lr), so
EnsureMaterial is only invoked when positions are present.

Comment on lines +126 to +163
// Handle material if provided
if (@params["materialPath"] != null)
{
Material mat = ManageVfxCommon.FindMaterialByPath(@params["materialPath"].ToString());
if (mat != null)
{
lr.sharedMaterial = mat;
changes.Add($"material={mat.name}");
}
else
{
McpLog.Warn($"Material not found: {@params["materialPath"]}");
}
}

// Handle positions if provided
if (@params["positions"] != null)
{
JArray posArr = @params["positions"] as JArray;
if (posArr != null && posArr.Count > 0)
{
var positions = new Vector3[posArr.Count];
for (int i = 0; i < posArr.Count; i++)
{
positions[i] = ManageVfxCommon.ParseVector3(posArr[i]);
}
lr.positionCount = positions.Length;
lr.SetPositions(positions);
changes.Add($"positions({positions.Length})");
}
}
else if (@params["positionCount"] != null)
{
int count = @params["positionCount"].ToObject<int>();
lr.positionCount = count;
changes.Add("positionCount");
}

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Empty positions arrays are ignored.

When positions is provided but empty, the branch does nothing and also blocks the positionCount fallback. This makes positions: [] unable to clear the line. Consider treating empty arrays as a valid clear.

🔧 Proposed fix
-            if (posArr != null && posArr.Count > 0)
+            if (posArr != null)
             {
                 var positions = new Vector3[posArr.Count];
                 for (int i = 0; i < posArr.Count; i++)
                 {
                     positions[i] = ManageVfxCommon.ParseVector3(posArr[i]);
                 }
                 lr.positionCount = positions.Length;
                 lr.SetPositions(positions);
                 changes.Add($"positions({positions.Length})");
             }
🤖 Prompt for AI Agents
In `@MCPForUnity/Editor/Tools/Vfx/LineWrite.cs` around lines 126 - 163, The code
currently treats an empty JArray for `@params`["positions"] as "do nothing" and
also prevents the positionCount fallback; update the positions handling in
LineWrite.cs so that when `@params`["positions"] is present you always handle it:
cast to JArray (posArr) and if posArr.Count > 0 parse and SetPositions as
before, otherwise clear the line by setting lr.positionCount = 0 (and/or
lr.SetPositions(new Vector3[0])) and record changes.Add("positions(0)"); also
change the subsequent positionCount check so it runs only when
`@params`["positions"] is null (i.e., keep it as an else branch based on presence
of `@params`["positions"] rather than posArr.Count).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@MCPForUnity/Editor/Tools/Vfx/ManageVFX.cs`:
- Around line 183-194: The catch in ExtractProperties references an undeclared
variable `ex`; update the catch clause to declare the exception (e.g., change
`catch (JsonException)` to `catch (JsonException ex)`) and pass that `ex` into
the thrown JsonException so the inner exception is available (keep the existing
throw that uses the message and `ex`). Locate this in the token.Type ==
JTokenType.String branch near the JObject parsing logic.

Comment on lines 183 to 194
if (token.Type == JTokenType.String)
{
try
{
return JToken.Parse(token.ToString()) as JObject;
}
catch (JsonException)
{
throw new JsonException(
$"Failed to parse 'properties' JSON string. Raw value: {token}",
ex);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix compile error in ExtractProperties catch.
ex is referenced but never declared, which breaks the build.

🐛 Proposed fix
-                catch (JsonException)
+                catch (JsonException ex)
                 {
                     throw new JsonException(  
                         $"Failed to parse 'properties' JSON string. Raw value: {token}",  
                         ex); 
                 }
🤖 Prompt for AI Agents
In `@MCPForUnity/Editor/Tools/Vfx/ManageVFX.cs` around lines 183 - 194, The catch
in ExtractProperties references an undeclared variable `ex`; update the catch
clause to declare the exception (e.g., change `catch (JsonException)` to `catch
(JsonException ex)`) and pass that `ex` into the thrown JsonException so the
inner exception is available (keep the existing throw that uses the message and
`ex`). Locate this in the token.Type == JTokenType.String branch near the
JObject parsing logic.

@Scriptwonder Scriptwonder merged commit bb56f78 into CoplayDev:beta Jan 26, 2026
1 check passed
@Scriptwonder Scriptwonder deleted the vfxOptimization branch January 26, 2026 00:35
@Scriptwonder Scriptwonder restored the vfxOptimization branch January 26, 2026 01:02
@Scriptwonder Scriptwonder deleted the vfxOptimization branch January 26, 2026 01:03
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.

1 participant