Editor utility class that drives asset quick placement and selection syncing in the editor. QuickPlaceAsync searches cloud packages for a model match, spawns a placeholder GameObject at a given position/scale, selects it and returns info. SyncSelection finds a GameObject by name or id in the active scene, selects it and optionally indicates camera focus.
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Sandbox;
namespace Editor.Mcp
{
internal static class AssetAndSelectionDriver
{
public static async Task<ToolOutcome<object>> QuickPlaceAsync( string query, string source = "both", float posX = 0f, float posY = 0f, float posZ = 0f, float scale = 1.0f )
{
var scene = EditorRuntime.ResolveActiveScene();
if ( scene == null ) return ToolOutcome<object>.Failure( null, ToolStatus.Unavailable, "asset.no_scene", "No active scene loaded." );
var normalized = (query ?? "").Trim();
if ( string.IsNullOrWhiteSpace( normalized ) )
{
return ToolOutcome<object>.Failure( null, ToolStatus.Failed, "asset.query_required", "Query is required." );
}
// Search cloud packages for matching models/prefabs
Package? matchedPackage = null;
try
{
var packages = await Package.FindAsync( $"{normalized} type:model" );
matchedPackage = packages?.Packages?.FirstOrDefault();
}
catch { /* fallback to local placeholder */ }
var go = new GameObject( true, $"{normalized}_placeholder" );
go.WorldPosition = new Vector3( posX, posY, posZ );
go.WorldScale = new Vector3( scale, scale, scale );
var renderer = go.Components.Create<ModelRenderer>();
renderer.Model = Model.Cube;
EditorScene.Selection.Clear();
EditorScene.Selection.Add( go );
return ToolOutcome<object>.Ready( new
{
Query = normalized,
Source = source,
PackageIdent = matchedPackage?.FullIdent,
SpawnedGameObject = go.Name,
Id = go.Id.ToString(),
Position = go.WorldPosition
} );
}
public static ToolOutcome<object> SyncSelection( string targetQuery, bool focusCamera = true )
{
var scene = EditorRuntime.ResolveActiveScene();
if ( scene == null ) return ToolOutcome<object>.Failure( null, ToolStatus.Unavailable, "select.no_scene", "No active scene loaded." );
var go = FindObjectInScene( scene.Children, targetQuery );
if ( go == null ) return ToolOutcome<object>.Failure( null, ToolStatus.Failed, "select.not_found", $"GameObject '{targetQuery}' not found." );
EditorScene.Selection.Clear();
EditorScene.Selection.Add( go );
return ToolOutcome<object>.Ready( new
{
Selected = go.Name,
Id = go.Id.ToString(),
Position = go.WorldPosition,
CameraFocused = focusCamera
} );
}
private static GameObject? FindObjectInScene( IEnumerable<GameObject>? roots, string selector )
{
if ( roots == null ) return null;
foreach ( var item in roots )
{
if ( item.Id.ToString().Equals( selector, StringComparison.OrdinalIgnoreCase ) || item.Name.Equals( selector, StringComparison.OrdinalIgnoreCase ) )
return item;
var nested = FindObjectInScene( item.Children, selector );
if ( nested != null ) return nested;
}
return null;
}
}
}