Asset store helper script + updated README (#521)

* Remove stray .meta file

* Add a new project that will do asset uploads

* Add asset store uploader

* refactor: Replace Debug.Log calls with McpLog helper across codebase

Standardize logging by replacing direct Debug.Log/LogWarning/LogError calls
with McpLog.Info/Warn/Error throughout helper classes and client registry.
Affected files:
- McpClientRegistry.cs
- GameObjectLookup.cs
- GameObjectSerializer.cs
- MaterialOps.cs
- McpConfigurationHelper.cs
- ObjectResolver.cs
- PropertyConversion.cs
- UnityJsonSerializer.cs
- UnityTypeResolver.cs

* feat: Add Asset Store release preparation script

Add prepare_unity_asset_store_release.py tool to automate Asset Store packaging:
- Stages temporary copy of MCPForUnity with Asset Store-specific edits
- Removes auto-popup setup window ([InitializeOnLoad] attribute)
- Renames menu entry to "Local Setup Window" for clarity
- Sets default HTTP base URL to hosted endpoint
- Defaults transport to HTTPRemote instead of HTTPLocal
- Supports dry-run mode and optional backup of existing Assets/MCPForUnity

* Show gif of MCP for Unity in Action

* Add shield with asset store link

* Update README to have asset store page unders installation section
main
Marcus Sanatan 2026-01-07 01:33:20 -04:00 committed by GitHub
parent 5511a2b8ad
commit 22e52664cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
775 changed files with 31969 additions and 205 deletions

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using MCPForUnity.Editor.Helpers;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
@ -47,7 +48,7 @@ namespace MCPForUnity.Editor.Clients
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"UnityMCP: Failed to instantiate configurator {type.Name}: {ex.Message}"); McpLog.Warn($"UnityMCP: Failed to instantiate configurator {type.Name}: {ex.Message}");
} }
} }

View File

@ -152,7 +152,7 @@ namespace MCPForUnity.Editor.Helpers
// Consider using by_name search with includeInactive if you need to find inactive objects. // Consider using by_name search with includeInactive if you need to find inactive objects.
if (includeInactive) if (includeInactive)
{ {
Debug.LogWarning("[GameObjectLookup] SearchByPath with includeInactive=true: " + McpLog.Warn("[GameObjectLookup] SearchByPath with includeInactive=true: " +
"GameObject.Find() cannot find inactive objects. Use by_name search instead."); "GameObject.Find() cannot find inactive objects. Use by_name search instead.");
} }
@ -224,7 +224,7 @@ namespace MCPForUnity.Editor.Helpers
Type componentType = FindComponentType(componentTypeName); Type componentType = FindComponentType(componentTypeName);
if (componentType == null) if (componentType == null)
{ {
Debug.LogWarning($"[GameObjectLookup] Component type '{componentTypeName}' not found."); McpLog.Warn($"[GameObjectLookup] Component type '{componentTypeName}' not found.");
yield break; yield break;
} }

View File

@ -122,7 +122,7 @@ namespace MCPForUnity.Editor.Helpers
public static object GetComponentData(Component c, bool includeNonPublicSerializedFields = true) public static object GetComponentData(Component c, bool includeNonPublicSerializedFields = true)
{ {
// --- Add Early Logging --- // --- Add Early Logging ---
// Debug.Log($"[GetComponentData] Starting for component: {c?.GetType()?.FullName ?? "null"} (ID: {c?.GetInstanceID() ?? 0})"); // McpLog.Info($"[GetComponentData] Starting for component: {c?.GetType()?.FullName ?? "null"} (ID: {c?.GetInstanceID() ?? 0})");
// --- End Early Logging --- // --- End Early Logging ---
if (c == null) return null; if (c == null) return null;
@ -132,7 +132,7 @@ namespace MCPForUnity.Editor.Helpers
if (componentType == typeof(Transform)) if (componentType == typeof(Transform))
{ {
Transform tr = c as Transform; Transform tr = c as Transform;
// Debug.Log($"[GetComponentData] Manually serializing Transform (ID: {tr.GetInstanceID()})"); // McpLog.Info($"[GetComponentData] Manually serializing Transform (ID: {tr.GetInstanceID()})");
return new Dictionary<string, object> return new Dictionary<string, object>
{ {
{ "typeName", componentType.FullName }, { "typeName", componentType.FullName },
@ -295,7 +295,7 @@ namespace MCPForUnity.Editor.Helpers
var serializablePropertiesOutput = new Dictionary<string, object>(); var serializablePropertiesOutput = new Dictionary<string, object>();
// --- Add Logging Before Property Loop --- // --- Add Logging Before Property Loop ---
// Debug.Log($"[GetComponentData] Starting property loop for {componentType.Name}..."); // McpLog.Info($"[GetComponentData] Starting property loop for {componentType.Name}...");
// --- End Logging Before Property Loop --- // --- End Logging Before Property Loop ---
// Use cached properties // Use cached properties
@ -313,7 +313,7 @@ namespace MCPForUnity.Editor.Helpers
// Also skip potentially problematic Matrix properties prone to cycles/errors // Also skip potentially problematic Matrix properties prone to cycles/errors
propName == "worldToLocalMatrix" || propName == "localToWorldMatrix") propName == "worldToLocalMatrix" || propName == "localToWorldMatrix")
{ {
// Debug.Log($"[GetComponentData] Explicitly skipping generic property: {propName}"); // Optional log // McpLog.Info($"[GetComponentData] Explicitly skipping generic property: {propName}"); // Optional log
skipProperty = true; skipProperty = true;
} }
// --- End Skip Generic Properties --- // --- End Skip Generic Properties ---
@ -330,7 +330,7 @@ namespace MCPForUnity.Editor.Helpers
propName == "previousViewProjectionMatrix" || propName == "previousViewProjectionMatrix" ||
propName == "cameraToWorldMatrix")) propName == "cameraToWorldMatrix"))
{ {
// Debug.Log($"[GetComponentData] Explicitly skipping Camera property: {propName}"); // McpLog.Info($"[GetComponentData] Explicitly skipping Camera property: {propName}");
skipProperty = true; skipProperty = true;
} }
// --- End Skip Camera Properties --- // --- End Skip Camera Properties ---
@ -342,7 +342,7 @@ namespace MCPForUnity.Editor.Helpers
propName == "worldToLocalMatrix" || propName == "worldToLocalMatrix" ||
propName == "localToWorldMatrix")) propName == "localToWorldMatrix"))
{ {
// Debug.Log($"[GetComponentData] Explicitly skipping Transform property: {propName}"); // McpLog.Info($"[GetComponentData] Explicitly skipping Transform property: {propName}");
skipProperty = true; skipProperty = true;
} }
// --- End Skip Transform Properties --- // --- End Skip Transform Properties ---
@ -356,7 +356,7 @@ namespace MCPForUnity.Editor.Helpers
try try
{ {
// --- Add detailed logging --- // --- Add detailed logging ---
// Debug.Log($"[GetComponentData] Accessing: {componentType.Name}.{propName}"); // McpLog.Info($"[GetComponentData] Accessing: {componentType.Name}.{propName}");
// --- End detailed logging --- // --- End detailed logging ---
// --- Special handling for material/mesh properties in edit mode --- // --- Special handling for material/mesh properties in edit mode ---
@ -392,12 +392,12 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (Exception) catch (Exception)
{ {
// Debug.LogWarning($"Could not read property {propName} on {componentType.Name}"); // McpLog.Warn($"Could not read property {propName} on {componentType.Name}");
} }
} }
// --- Add Logging Before Field Loop --- // --- Add Logging Before Field Loop ---
// Debug.Log($"[GetComponentData] Starting field loop for {componentType.Name}..."); // McpLog.Info($"[GetComponentData] Starting field loop for {componentType.Name}...");
// --- End Logging Before Field Loop --- // --- End Logging Before Field Loop ---
// Use cached fields // Use cached fields
@ -406,7 +406,7 @@ namespace MCPForUnity.Editor.Helpers
try try
{ {
// --- Add detailed logging for fields --- // --- Add detailed logging for fields ---
// Debug.Log($"[GetComponentData] Accessing Field: {componentType.Name}.{fieldInfo.Name}"); // McpLog.Info($"[GetComponentData] Accessing Field: {componentType.Name}.{fieldInfo.Name}");
// --- End detailed logging for fields --- // --- End detailed logging for fields ---
object value = fieldInfo.GetValue(c); object value = fieldInfo.GetValue(c);
string fieldName = fieldInfo.Name; string fieldName = fieldInfo.Name;
@ -415,7 +415,7 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (Exception) catch (Exception)
{ {
// Debug.LogWarning($"Could not read field {fieldInfo.Name} on {componentType.Name}"); // McpLog.Warn($"Could not read field {fieldInfo.Name} on {componentType.Name}");
} }
} }
// --- End Use cached metadata --- // --- End Use cached metadata ---
@ -452,7 +452,7 @@ namespace MCPForUnity.Editor.Helpers
catch (Exception e) catch (Exception e)
{ {
// Catch potential errors during JToken conversion or addition to dictionary // Catch potential errors during JToken conversion or addition to dictionary
Debug.LogWarning($"[AddSerializableValue] Error processing value for '{name}' (Type: {type.FullName}): {e.Message}. Skipping."); McpLog.Warn($"[AddSerializableValue] Error processing value for '{name}' (Type: {type.FullName}): {e.Message}. Skipping.");
} }
} }
@ -508,7 +508,7 @@ namespace MCPForUnity.Editor.Helpers
{ {
return jValue.Value; return jValue.Value;
} }
// Debug.LogWarning($"Unsupported JTokenType encountered: {token.Type}. Returning null."); // McpLog.Warn($"Unsupported JTokenType encountered: {token.Type}. Returning null.");
return null; return null;
} }
} }
@ -545,12 +545,12 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (JsonSerializationException e) catch (JsonSerializationException e)
{ {
Debug.LogWarning($"[GameObjectSerializer] Newtonsoft.Json Error serializing value of type {type.FullName}: {e.Message}. Skipping property/field."); McpLog.Warn($"[GameObjectSerializer] Newtonsoft.Json Error serializing value of type {type.FullName}: {e.Message}. Skipping property/field.");
return null; // Indicate serialization failure return null; // Indicate serialization failure
} }
catch (Exception e) // Catch other unexpected errors catch (Exception e) // Catch other unexpected errors
{ {
Debug.LogWarning($"[GameObjectSerializer] Unexpected error serializing value of type {type.FullName}: {e}. Skipping property/field."); McpLog.Warn($"[GameObjectSerializer] Unexpected error serializing value of type {type.FullName}: {e}. Skipping property/field.");
return null; // Indicate serialization failure return null; // Indicate serialization failure
} }
} }

View File

@ -1,11 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using MCPForUnity.Editor.Tools;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEditor; using UnityEditor;
using MCPForUnity.Editor.Tools; using UnityEngine;
namespace MCPForUnity.Editor.Helpers namespace MCPForUnity.Editor.Helpers
{ {
@ -63,7 +63,7 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"[MaterialOps] Failed to parse color for property '{propName}': {ex.Message}"); McpLog.Warn($"[MaterialOps] Failed to parse color for property '{propName}': {ex.Message}");
} }
} }
} }
@ -81,7 +81,7 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"[MaterialOps] Failed to parse color array: {ex.Message}"); McpLog.Warn($"[MaterialOps] Failed to parse color array: {ex.Message}");
} }
} }
@ -104,7 +104,7 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"[MaterialOps] Failed to set float property '{propName}': {ex.Message}"); McpLog.Warn($"[MaterialOps] Failed to set float property '{propName}': {ex.Message}");
} }
} }
} }
@ -123,7 +123,7 @@ namespace MCPForUnity.Editor.Helpers
// Use ResolvePropertyName to handle aliases even for structured texture names // Use ResolvePropertyName to handle aliases even for structured texture names
string candidateName = string.IsNullOrEmpty(rawName) ? "_BaseMap" : rawName; string candidateName = string.IsNullOrEmpty(rawName) ? "_BaseMap" : rawName;
string targetProp = ResolvePropertyName(mat, candidateName); string targetProp = ResolvePropertyName(mat, candidateName);
if (!string.IsNullOrEmpty(targetProp) && mat.HasProperty(targetProp)) if (!string.IsNullOrEmpty(targetProp) && mat.HasProperty(targetProp))
{ {
if (mat.GetTexture(targetProp) != newTex) if (mat.GetTexture(targetProp) != newTex)
@ -231,17 +231,17 @@ namespace MCPForUnity.Editor.Helpers
{ {
if (material.HasProperty(propertyName)) if (material.HasProperty(propertyName))
{ {
try { material.SetColor(propertyName, ParseColor(value, serializer)); return true; } try { material.SetColor(propertyName, ParseColor(value, serializer)); return true; }
catch (Exception ex) catch (Exception ex)
{ {
// Log at Debug level since we'll try other conversions // Log at Debug level since we'll try other conversions
Debug.Log($"[MaterialOps] SetColor attempt for '{propertyName}' failed: {ex.Message}"); McpLog.Info($"[MaterialOps] SetColor attempt for '{propertyName}' failed: {ex.Message}");
} }
try { Vector4 vec = value.ToObject<Vector4>(serializer); material.SetVector(propertyName, vec); return true; } try { Vector4 vec = value.ToObject<Vector4>(serializer); material.SetVector(propertyName, vec); return true; }
catch (Exception ex) catch (Exception ex)
{ {
Debug.Log($"[MaterialOps] SetVector (Vec4) attempt for '{propertyName}' failed: {ex.Message}"); McpLog.Info($"[MaterialOps] SetVector (Vec4) attempt for '{propertyName}' failed: {ex.Message}");
} }
} }
} }
@ -249,10 +249,10 @@ namespace MCPForUnity.Editor.Helpers
{ {
if (material.HasProperty(propertyName)) if (material.HasProperty(propertyName))
{ {
try { material.SetColor(propertyName, ParseColor(value, serializer)); return true; } try { material.SetColor(propertyName, ParseColor(value, serializer)); return true; }
catch (Exception ex) catch (Exception ex)
{ {
Debug.Log($"[MaterialOps] SetColor (Vec3) attempt for '{propertyName}' failed: {ex.Message}"); McpLog.Info($"[MaterialOps] SetColor (Vec3) attempt for '{propertyName}' failed: {ex.Message}");
} }
} }
} }
@ -260,10 +260,10 @@ namespace MCPForUnity.Editor.Helpers
{ {
if (material.HasProperty(propertyName)) if (material.HasProperty(propertyName))
{ {
try { Vector2 vec = value.ToObject<Vector2>(serializer); material.SetVector(propertyName, vec); return true; } try { Vector2 vec = value.ToObject<Vector2>(serializer); material.SetVector(propertyName, vec); return true; }
catch (Exception ex) catch (Exception ex)
{ {
Debug.Log($"[MaterialOps] SetVector (Vec2) attempt for '{propertyName}' failed: {ex.Message}"); McpLog.Info($"[MaterialOps] SetVector (Vec2) attempt for '{propertyName}' failed: {ex.Message}");
} }
} }
} }
@ -273,10 +273,10 @@ namespace MCPForUnity.Editor.Helpers
if (!material.HasProperty(propertyName)) if (!material.HasProperty(propertyName))
return false; return false;
try { material.SetFloat(propertyName, value.ToObject<float>(serializer)); return true; } try { material.SetFloat(propertyName, value.ToObject<float>(serializer)); return true; }
catch (Exception ex) catch (Exception ex)
{ {
Debug.Log($"[MaterialOps] SetFloat attempt for '{propertyName}' failed: {ex.Message}"); McpLog.Info($"[MaterialOps] SetFloat attempt for '{propertyName}' failed: {ex.Message}");
} }
} }
else if (value.Type == JTokenType.Boolean) else if (value.Type == JTokenType.Boolean)
@ -284,10 +284,10 @@ namespace MCPForUnity.Editor.Helpers
if (!material.HasProperty(propertyName)) if (!material.HasProperty(propertyName))
return false; return false;
try { material.SetFloat(propertyName, value.ToObject<bool>(serializer) ? 1f : 0f); return true; } try { material.SetFloat(propertyName, value.ToObject<bool>(serializer) ? 1f : 0f); return true; }
catch (Exception ex) catch (Exception ex)
{ {
Debug.Log($"[MaterialOps] SetFloat (bool) attempt for '{propertyName}' failed: {ex.Message}"); McpLog.Info($"[MaterialOps] SetFloat (bool) attempt for '{propertyName}' failed: {ex.Message}");
} }
} }
else if (value.Type == JTokenType.String) else if (value.Type == JTokenType.String)
@ -298,16 +298,16 @@ namespace MCPForUnity.Editor.Helpers
string path = value.ToString(); string path = value.ToString();
if (!string.IsNullOrEmpty(path) && path.Contains("/")) // Heuristic: paths usually have slashes if (!string.IsNullOrEmpty(path) && path.Contains("/")) // Heuristic: paths usually have slashes
{ {
// We need to handle texture assignment here. // We need to handle texture assignment here.
// Since we don't have easy access to AssetDatabase here directly without using UnityEditor namespace (which is imported), // Since we don't have easy access to AssetDatabase here directly without using UnityEditor namespace (which is imported),
// we can try to load it. // we can try to load it.
var sanitizedPath = AssetPathUtility.SanitizeAssetPath(path); var sanitizedPath = AssetPathUtility.SanitizeAssetPath(path);
Texture tex = AssetDatabase.LoadAssetAtPath<Texture>(sanitizedPath); Texture tex = AssetDatabase.LoadAssetAtPath<Texture>(sanitizedPath);
if (tex != null && material.HasProperty(propertyName)) if (tex != null && material.HasProperty(propertyName))
{ {
material.SetTexture(propertyName, tex); material.SetTexture(propertyName, tex);
return true; return true;
} }
} }
} }
catch (Exception ex) catch (Exception ex)
@ -315,10 +315,10 @@ namespace MCPForUnity.Editor.Helpers
McpLog.Warn($"SetTexture (string path) for '{propertyName}' failed: {ex.Message}"); McpLog.Warn($"SetTexture (string path) for '{propertyName}' failed: {ex.Message}");
} }
} }
if (value.Type == JTokenType.Object) if (value.Type == JTokenType.Object)
{ {
try try
{ {
Texture texture = value.ToObject<Texture>(serializer); Texture texture = value.ToObject<Texture>(serializer);
if (texture != null && material.HasProperty(propertyName)) if (texture != null && material.HasProperty(propertyName))
@ -333,7 +333,7 @@ namespace MCPForUnity.Editor.Helpers
} }
} }
Debug.LogWarning( McpLog.Warn(
$"[MaterialOps] Unsupported or failed conversion for material property '{propertyName}' from value: {value.ToString(Formatting.None)}" $"[MaterialOps] Unsupported or failed conversion for material property '{propertyName}' from value: {value.ToString(Formatting.None)}"
); );
return false; return false;
@ -382,14 +382,14 @@ namespace MCPForUnity.Editor.Helpers
throw new ArgumentException("Color array must have 3 or 4 elements."); throw new ArgumentException("Color array must have 3 or 4 elements.");
} }
} }
try try
{ {
return token.ToObject<Color>(serializer); return token.ToObject<Color>(serializer);
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"[MaterialOps] Failed to parse color from token: {ex.Message}"); McpLog.Warn($"[MaterialOps] Failed to parse color from token: {ex.Message}");
throw; throw;
} }
} }

View File

@ -49,7 +49,7 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogWarning($"Error reading existing config: {e.Message}."); McpLog.Warn($"Error reading existing config: {e.Message}.");
} }
} }
@ -71,7 +71,7 @@ namespace MCPForUnity.Editor.Helpers
// If user has partial/invalid JSON (e.g., mid-edit), start from a fresh object // If user has partial/invalid JSON (e.g., mid-edit), start from a fresh object
if (!string.IsNullOrWhiteSpace(existingJson)) if (!string.IsNullOrWhiteSpace(existingJson))
{ {
Debug.LogWarning("UnityMCP: Configuration file could not be parsed; rewriting server block."); McpLog.Warn("UnityMCP: Configuration file could not be parsed; rewriting server block.");
} }
existingConfig = new JObject(); existingConfig = new JObject();
} }
@ -137,7 +137,7 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogWarning($"UnityMCP: Failed to read Codex config '{configPath}': {e.Message}"); McpLog.Warn($"UnityMCP: Failed to read Codex config '{configPath}': {e.Message}");
existingToml = string.Empty; existingToml = string.Empty;
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using MCPForUnity.Editor.Helpers;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
@ -39,7 +40,7 @@ namespace MCPForUnity.Editor.Helpers
if (string.IsNullOrEmpty(findTerm)) if (string.IsNullOrEmpty(findTerm))
{ {
Debug.LogWarning("[ObjectResolver] Find instruction missing 'find' term."); McpLog.Warn("[ObjectResolver] Find instruction missing 'find' term.");
return null; return null;
} }
@ -85,20 +86,20 @@ namespace MCPForUnity.Editor.Helpers
} }
else else
{ {
Debug.LogWarning($"[ObjectResolver] Could not find component type '{componentName}'. Falling back to target type '{targetType.Name}'."); McpLog.Warn($"[ObjectResolver] Could not find component type '{componentName}'. Falling back to target type '{targetType.Name}'.");
} }
} }
Component foundComp = foundGo.GetComponent(componentToGetType); Component foundComp = foundGo.GetComponent(componentToGetType);
if (foundComp == null) if (foundComp == null)
{ {
Debug.LogWarning($"[ObjectResolver] Found GameObject '{foundGo.name}' but could not find component of type '{componentToGetType.Name}'."); McpLog.Warn($"[ObjectResolver] Found GameObject '{foundGo.name}' but could not find component of type '{componentToGetType.Name}'.");
} }
return foundComp; return foundComp;
} }
else else
{ {
Debug.LogWarning($"[ObjectResolver] Find instruction handling not implemented for target type: {targetType.Name}"); McpLog.Warn($"[ObjectResolver] Find instruction handling not implemented for target type: {targetType.Name}");
return null; return null;
} }
} }
@ -190,7 +191,7 @@ namespace MCPForUnity.Editor.Helpers
} }
else if (guids.Length > 1) else if (guids.Length > 1)
{ {
Debug.LogWarning($"[ObjectResolver] Ambiguous asset find: Found {guids.Length} assets matching filter '{searchFilter}'. Provide a full path or unique name."); McpLog.Warn($"[ObjectResolver] Ambiguous asset find: Found {guids.Length} assets matching filter '{searchFilter}'. Provide a full path or unique name.");
return null; return null;
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using MCPForUnity.Editor.Helpers;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
@ -24,7 +25,7 @@ namespace MCPForUnity.Editor.Helpers
{ {
if (targetType.IsValueType && Nullable.GetUnderlyingType(targetType) == null) if (targetType.IsValueType && Nullable.GetUnderlyingType(targetType) == null)
{ {
Debug.LogWarning($"[PropertyConversion] Cannot assign null to non-nullable value type {targetType.Name}. Returning default value."); McpLog.Warn($"[PropertyConversion] Cannot assign null to non-nullable value type {targetType.Name}. Returning default value.");
return Activator.CreateInstance(targetType); return Activator.CreateInstance(targetType);
} }
return null; return null;
@ -37,7 +38,7 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogError($"Error converting token to {targetType.FullName}: {ex.Message}\nToken: {token.ToString(Formatting.None)}"); McpLog.Error($"Error converting token to {targetType.FullName}: {ex.Message}\nToken: {token.ToString(Formatting.None)}");
throw; throw;
} }
} }
@ -82,7 +83,7 @@ namespace MCPForUnity.Editor.Helpers
if (loadedAsset == null) if (loadedAsset == null)
{ {
Debug.LogWarning($"[PropertyConversion] Could not load asset of type {targetType.Name} from path: {assetPath}"); McpLog.Warn($"[PropertyConversion] Could not load asset of type {targetType.Name} from path: {assetPath}");
} }
return loadedAsset; return loadedAsset;

View File

@ -44,7 +44,7 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"[VectorParsing] Failed to parse Vector3 from '{token}': {ex.Message}"); McpLog.Warn($"[VectorParsing] Failed to parse Vector3 from '{token}': {ex.Message}");
} }
return null; return null;
@ -90,7 +90,7 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"[VectorParsing] Failed to parse Vector2 from '{token}': {ex.Message}"); McpLog.Warn($"[VectorParsing] Failed to parse Vector2 from '{token}': {ex.Message}");
} }
return null; return null;
@ -162,7 +162,7 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"[VectorParsing] Failed to parse Quaternion from '{token}': {ex.Message}"); McpLog.Warn($"[VectorParsing] Failed to parse Quaternion from '{token}': {ex.Message}");
} }
return null; return null;
@ -218,7 +218,7 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"[VectorParsing] Failed to parse Color from '{token}': {ex.Message}"); McpLog.Warn($"[VectorParsing] Failed to parse Color from '{token}': {ex.Message}");
} }
return null; return null;
@ -260,7 +260,7 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"[VectorParsing] Failed to parse Rect from '{token}': {ex.Message}"); McpLog.Warn($"[VectorParsing] Failed to parse Rect from '{token}': {ex.Message}");
} }
return null; return null;
@ -286,7 +286,7 @@ namespace MCPForUnity.Editor.Helpers
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"[VectorParsing] Failed to parse Bounds from '{token}': {ex.Message}"); McpLog.Warn($"[VectorParsing] Failed to parse Bounds from '{token}': {ex.Message}");
} }
return null; return null;

View File

@ -44,7 +44,7 @@ namespace MCPForUnity.Editor.Resources.Editor
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"Could not get info for window {window.GetType().Name}: {ex.Message}"); McpLog.Warn($"Could not get info for window {window.GetType().Name}: {ex.Message}");
} }
} }

View File

@ -58,7 +58,7 @@ namespace MCPForUnity.Editor.Resources.Scene
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError($"[GameObjectResource] Error getting GameObject: {e}"); McpLog.Error($"[GameObjectResource] Error getting GameObject: {e}");
return new ErrorResponse($"Error getting GameObject: {e.Message}"); return new ErrorResponse($"Error getting GameObject: {e.Message}");
} }
} }
@ -199,7 +199,7 @@ namespace MCPForUnity.Editor.Resources.Scene
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError($"[GameObjectComponentsResource] Error getting components: {e}"); McpLog.Error($"[GameObjectComponentsResource] Error getting components: {e}");
return new ErrorResponse($"Error getting components: {e.Message}"); return new ErrorResponse($"Error getting components: {e.Message}");
} }
} }
@ -276,7 +276,7 @@ namespace MCPForUnity.Editor.Resources.Scene
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError($"[GameObjectComponentResource] Error getting component: {e}"); McpLog.Error($"[GameObjectComponentResource] Error getting component: {e}");
return new ErrorResponse($"Error getting component: {e.Message}"); return new ErrorResponse($"Error getting component: {e.Message}");
} }
} }

View File

@ -64,7 +64,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (System.Exception ex) catch (System.Exception ex)
{ {
Debug.LogError($"[FindGameObjects] Error searching GameObjects: {ex.Message}"); McpLog.Error($"[FindGameObjects] Error searching GameObjects: {ex.Message}");
return new ErrorResponse($"Error searching GameObjects: {ex.Message}"); return new ErrorResponse($"Error searching GameObjects: {ex.Message}");
} }
} }

View File

@ -1,3 +1,4 @@
using MCPForUnity.Editor.Helpers;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using UnityEngine; using UnityEngine;
@ -22,7 +23,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Newtonsoft.Json.JsonReaderException e) catch (Newtonsoft.Json.JsonReaderException e)
{ {
Debug.LogWarning($"[MCP] Could not parse '{paramName}' JSON string: {e.Message}"); McpLog.Warn($"[MCP] Could not parse '{paramName}' JSON string: {e.Message}");
} }
} }
} }

View File

@ -74,7 +74,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogWarning($"[ManageAsset] Could not parse 'properties' JSON string: {e.Message}"); McpLog.Warn($"[ManageAsset] Could not parse 'properties' JSON string: {e.Message}");
} }
} }
@ -119,7 +119,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError($"[ManageAsset] Action '{action}' failed for path '{path}': {e}"); McpLog.Error($"[ManageAsset] Action '{action}' failed for path '{path}': {e}");
return new ErrorResponse( return new ErrorResponse(
$"Internal error processing action '{action}' on '{path}': {e.Message}" $"Internal error processing action '{action}' on '{path}': {e.Message}"
); );
@ -143,7 +143,7 @@ namespace MCPForUnity.Editor.Tools
// applying properties via reflection or specific methods, saving, then reimporting. // applying properties via reflection or specific methods, saving, then reimporting.
if (properties != null && properties.HasValues) if (properties != null && properties.HasValues)
{ {
Debug.LogWarning( McpLog.Warn(
"[ManageAsset.Reimport] Modifying importer properties before reimport is not fully implemented yet." "[ManageAsset.Reimport] Modifying importer properties before reimport is not fully implemented yet."
); );
// AssetImporter importer = AssetImporter.GetAtPath(fullPath); // AssetImporter importer = AssetImporter.GetAtPath(fullPath);
@ -376,7 +376,7 @@ namespace MCPForUnity.Editor.Tools
// Only warn about resolution failure if component also not found // Only warn about resolution failure if component also not found
if (targetComponent == null && !resolved) if (targetComponent == null && !resolved)
{ {
Debug.LogWarning( McpLog.Warn(
$"[ManageAsset.ModifyAsset] Failed to resolve component '{componentName}' on '{gameObject.name}': {compError}" $"[ManageAsset.ModifyAsset] Failed to resolve component '{componentName}' on '{gameObject.name}': {compError}"
); );
} }
@ -393,7 +393,7 @@ namespace MCPForUnity.Editor.Tools
else else
{ {
// Log a warning if a specified component couldn't be found // Log a warning if a specified component couldn't be found
Debug.LogWarning( McpLog.Warn(
$"[ManageAsset.ModifyAsset] Component '{componentName}' not found on GameObject '{gameObject.name}' in asset '{fullPath}'. Skipping modification for this component." $"[ManageAsset.ModifyAsset] Component '{componentName}' not found on GameObject '{gameObject.name}' in asset '{fullPath}'. Skipping modification for this component."
); );
} }
@ -403,7 +403,7 @@ namespace MCPForUnity.Editor.Tools
// Log a warning if the structure isn't {"ComponentName": {"prop": value}} // Log a warning if the structure isn't {"ComponentName": {"prop": value}}
// We could potentially try to apply this property directly to the GameObject here if needed, // We could potentially try to apply this property directly to the GameObject here if needed,
// but the primary goal is component modification. // but the primary goal is component modification.
Debug.LogWarning( McpLog.Warn(
$"[ManageAsset.ModifyAsset] Property '{prop.Name}' for GameObject modification should have a JSON object value containing component properties. Value was: {prop.Value.Type}. Skipping." $"[ManageAsset.ModifyAsset] Property '{prop.Name}' for GameObject modification should have a JSON object value containing component properties. Value was: {prop.Value.Type}. Skipping."
); );
} }
@ -444,7 +444,7 @@ namespace MCPForUnity.Editor.Tools
} }
else else
{ {
Debug.LogWarning($"Could not get TextureImporter for {fullPath}."); McpLog.Warn($"Could not get TextureImporter for {fullPath}.");
} }
} }
// TODO: Add modification logic for other common asset types (Models, AudioClips importers, etc.) // TODO: Add modification logic for other common asset types (Models, AudioClips importers, etc.)
@ -452,7 +452,7 @@ namespace MCPForUnity.Editor.Tools
{ {
// This block handles non-GameObject/Material/ScriptableObject/Texture assets. // This block handles non-GameObject/Material/ScriptableObject/Texture assets.
// Attempts to apply properties directly to the asset itself. // Attempts to apply properties directly to the asset itself.
Debug.LogWarning( McpLog.Warn(
$"[ManageAsset.ModifyAsset] Asset type '{asset.GetType().Name}' at '{fullPath}' is not explicitly handled for component modification. Attempting generic property setting on the asset itself." $"[ManageAsset.ModifyAsset] Asset type '{asset.GetType().Name}' at '{fullPath}' is not explicitly handled for component modification. Attempting generic property setting on the asset itself."
); );
modified |= ApplyObjectProperties(asset, properties); modified |= ApplyObjectProperties(asset, properties);
@ -486,7 +486,7 @@ namespace MCPForUnity.Editor.Tools
catch (Exception e) catch (Exception e)
{ {
// Log the detailed error internally // Log the detailed error internally
Debug.LogError($"[ManageAsset] Action 'modify' failed for path '{path}': {e}"); McpLog.Error($"[ManageAsset] Action 'modify' failed for path '{path}': {e}");
// Return a user-friendly error message // Return a user-friendly error message
return new ErrorResponse($"Failed to modify asset '{fullPath}': {e.Message}"); return new ErrorResponse($"Failed to modify asset '{fullPath}': {e.Message}");
} }
@ -648,7 +648,7 @@ namespace MCPForUnity.Editor.Tools
{ {
// Maybe the user provided a file path instead of a folder? // Maybe the user provided a file path instead of a folder?
// We could search in the containing folder, or return an error. // We could search in the containing folder, or return an error.
Debug.LogWarning( McpLog.Warn(
$"Search path '{folderScope[0]}' is not a valid folder. Searching entire project." $"Search path '{folderScope[0]}' is not a valid folder. Searching entire project."
); );
folderScope = null; // Search everywhere if path isn't a folder folderScope = null; // Search everywhere if path isn't a folder
@ -671,7 +671,7 @@ namespace MCPForUnity.Editor.Tools
} }
else else
{ {
Debug.LogWarning( McpLog.Warn(
$"Could not parse filterDateAfter: '{filterDateAfterStr}'. Expected ISO 8601 format." $"Could not parse filterDateAfter: '{filterDateAfterStr}'. Expected ISO 8601 format."
); );
} }
@ -816,7 +816,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError( McpLog.Error(
$"[ManageAsset.GetComponentsFromAsset] Error getting components for '{fullPath}': {e}" $"[ManageAsset.GetComponentsFromAsset] Error getting components for '{fullPath}': {e}"
); );
return new ErrorResponse( return new ErrorResponse(
@ -1018,7 +1018,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning( McpLog.Warn(
$"[SetPropertyOrField] Failed to set '{memberName}' on {type.Name}: {ex.Message}" $"[SetPropertyOrField] Failed to set '{memberName}' on {type.Name}: {ex.Message}"
); );
} }
@ -1081,7 +1081,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning( McpLog.Warn(
$"Failed to generate readable preview for '{path}': {ex.Message}. Preview might not be readable." $"Failed to generate readable preview for '{path}': {ex.Message}. Preview might not be readable."
); );
// Fallback: Try getting static preview if available? // Fallback: Try getting static preview if available?
@ -1090,7 +1090,7 @@ namespace MCPForUnity.Editor.Tools
} }
else else
{ {
Debug.LogWarning( McpLog.Warn(
$"Could not get asset preview for {path} (Type: {assetType?.Name}). Is it supported?" $"Could not get asset preview for {path} (Type: {assetType?.Name}). Is it supported?"
); );
} }

View File

@ -58,7 +58,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError($"[ManageComponents] Action '{action}' failed: {e}"); McpLog.Error($"[ManageComponents] Action '{action}' failed: {e}");
return new ErrorResponse($"Internal error processing action '{action}': {e.Message}"); return new ErrorResponse($"Internal error processing action '{action}': {e.Message}");
} }
} }
@ -303,7 +303,7 @@ namespace MCPForUnity.Editor.Tools
if (errors.Count > 0) if (errors.Count > 0)
{ {
Debug.LogWarning($"[ManageComponents] Some properties failed to set on {component.GetType().Name}: {string.Join(", ", errors)}"); McpLog.Warn($"[ManageComponents] Some properties failed to set on {component.GetType().Name}: {string.Join(", ", errors)}");
} }
} }
@ -321,7 +321,7 @@ namespace MCPForUnity.Editor.Tools
return null; // Success return null; // Success
} }
Debug.LogWarning($"[ManageComponents] {error}"); McpLog.Warn($"[ManageComponents] {error}");
return error; return error;
} }

View File

@ -72,7 +72,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogWarning($"[ManageGameObject] Could not parse 'componentProperties' JSON string: {e.Message}"); McpLog.Warn($"[ManageGameObject] Could not parse 'componentProperties' JSON string: {e.Message}");
} }
} }
@ -116,7 +116,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError($"[ManageGameObject] Action '{action}' failed: {e}"); McpLog.Error($"[ManageGameObject] Action '{action}' failed: {e}");
return new ErrorResponse($"Internal error processing action '{action}': {e.Message}"); return new ErrorResponse($"Internal error processing action '{action}': {e.Message}");
} }
} }
@ -149,7 +149,7 @@ namespace MCPForUnity.Editor.Tools
) )
{ {
string prefabNameOnly = prefabPath; string prefabNameOnly = prefabPath;
Debug.Log( McpLog.Info(
$"[ManageGameObject.Create] Searching for prefab named: '{prefabNameOnly}'" $"[ManageGameObject.Create] Searching for prefab named: '{prefabNameOnly}'"
); );
string[] guids = AssetDatabase.FindAssets($"t:Prefab {prefabNameOnly}"); string[] guids = AssetDatabase.FindAssets($"t:Prefab {prefabNameOnly}");
@ -172,7 +172,7 @@ namespace MCPForUnity.Editor.Tools
else // Exactly one found else // Exactly one found
{ {
prefabPath = AssetDatabase.GUIDToAssetPath(guids[0]); // Update prefabPath with the full path prefabPath = AssetDatabase.GUIDToAssetPath(guids[0]); // Update prefabPath with the full path
Debug.Log( McpLog.Info(
$"[ManageGameObject.Create] Found unique prefab at path: '{prefabPath}'" $"[ManageGameObject.Create] Found unique prefab at path: '{prefabPath}'"
); );
} }
@ -180,7 +180,7 @@ namespace MCPForUnity.Editor.Tools
else if (!prefabPath.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) else if (!prefabPath.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase))
{ {
// If it looks like a path but doesn't end with .prefab, assume user forgot it and append it. // If it looks like a path but doesn't end with .prefab, assume user forgot it and append it.
Debug.LogWarning( McpLog.Warn(
$"[ManageGameObject.Create] Provided prefabPath '{prefabPath}' does not end with .prefab. Assuming it's missing and appending." $"[ManageGameObject.Create] Provided prefabPath '{prefabPath}' does not end with .prefab. Assuming it's missing and appending."
); );
prefabPath += ".prefab"; prefabPath += ".prefab";
@ -200,7 +200,7 @@ namespace MCPForUnity.Editor.Tools
if (newGo == null) if (newGo == null)
{ {
// This might happen if the asset exists but isn't a valid GameObject prefab somehow // This might happen if the asset exists but isn't a valid GameObject prefab somehow
Debug.LogError( McpLog.Error(
$"[ManageGameObject.Create] Failed to instantiate prefab at '{prefabPath}', asset might be corrupted or not a GameObject." $"[ManageGameObject.Create] Failed to instantiate prefab at '{prefabPath}', asset might be corrupted or not a GameObject."
); );
return new ErrorResponse( return new ErrorResponse(
@ -217,7 +217,7 @@ namespace MCPForUnity.Editor.Tools
newGo, newGo,
$"Instantiate Prefab '{prefabAsset.name}' as '{newGo.name}'" $"Instantiate Prefab '{prefabAsset.name}' as '{newGo.name}'"
); );
Debug.Log( McpLog.Info(
$"[ManageGameObject.Create] Instantiated prefab '{prefabAsset.name}' from path '{prefabPath}' as '{newGo.name}'." $"[ManageGameObject.Create] Instantiated prefab '{prefabAsset.name}' from path '{prefabPath}' as '{newGo.name}'."
); );
} }
@ -232,7 +232,7 @@ namespace MCPForUnity.Editor.Tools
{ {
// Only return error if prefabPath was specified but not found. // Only return error if prefabPath was specified but not found.
// If prefabPath was empty/null, we proceed to create primitive/empty. // If prefabPath was empty/null, we proceed to create primitive/empty.
Debug.LogWarning( McpLog.Warn(
$"[ManageGameObject.Create] Prefab asset not found at path: '{prefabPath}'. Will proceed to create new object if specified." $"[ManageGameObject.Create] Prefab asset not found at path: '{prefabPath}'. Will proceed to create new object if specified."
); );
// Do not return error here, allow fallback to primitive/empty creation // Do not return error here, allow fallback to primitive/empty creation
@ -337,7 +337,7 @@ namespace MCPForUnity.Editor.Tools
// Check if tag exists first (Unity doesn't throw exceptions for undefined tags, just logs a warning) // Check if tag exists first (Unity doesn't throw exceptions for undefined tags, just logs a warning)
if (tag != "Untagged" && !System.Linq.Enumerable.Contains(InternalEditorUtility.tags, tag)) if (tag != "Untagged" && !System.Linq.Enumerable.Contains(InternalEditorUtility.tags, tag))
{ {
Debug.Log($"[ManageGameObject.Create] Tag '{tag}' not found. Creating it."); McpLog.Info($"[ManageGameObject.Create] Tag '{tag}' not found. Creating it.");
try try
{ {
InternalEditorUtility.AddTag(tag); InternalEditorUtility.AddTag(tag);
@ -371,7 +371,7 @@ namespace MCPForUnity.Editor.Tools
} }
else else
{ {
Debug.LogWarning( McpLog.Warn(
$"[ManageGameObject.Create] Layer '{layerName}' not found. Using default layer." $"[ManageGameObject.Create] Layer '{layerName}' not found. Using default layer."
); );
} }
@ -406,7 +406,7 @@ namespace MCPForUnity.Editor.Tools
} }
else else
{ {
Debug.LogWarning( McpLog.Warn(
$"[ManageGameObject] Invalid component format in componentsToAdd: {compToken}" $"[ManageGameObject] Invalid component format in componentsToAdd: {compToken}"
); );
} }
@ -430,7 +430,7 @@ namespace MCPForUnity.Editor.Tools
// Ensure the *saving* path ends with .prefab // Ensure the *saving* path ends with .prefab
if (!finalPrefabPath.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) if (!finalPrefabPath.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase))
{ {
Debug.Log( McpLog.Info(
$"[ManageGameObject.Create] Appending .prefab extension to save path: '{finalPrefabPath}' -> '{finalPrefabPath}.prefab'" $"[ManageGameObject.Create] Appending .prefab extension to save path: '{finalPrefabPath}' -> '{finalPrefabPath}.prefab'"
); );
finalPrefabPath += ".prefab"; finalPrefabPath += ".prefab";
@ -447,7 +447,7 @@ namespace MCPForUnity.Editor.Tools
{ {
System.IO.Directory.CreateDirectory(directoryPath); System.IO.Directory.CreateDirectory(directoryPath);
AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); // Refresh asset database to recognize the new folder AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); // Refresh asset database to recognize the new folder
Debug.Log( McpLog.Info(
$"[ManageGameObject.Create] Created directory for prefab: {directoryPath}" $"[ManageGameObject.Create] Created directory for prefab: {directoryPath}"
); );
} }
@ -466,7 +466,7 @@ namespace MCPForUnity.Editor.Tools
$"Failed to save GameObject '{name}' as prefab at '{finalPrefabPath}'. Check path and permissions." $"Failed to save GameObject '{name}' as prefab at '{finalPrefabPath}'. Check path and permissions."
); );
} }
Debug.Log( McpLog.Info(
$"[ManageGameObject.Create] GameObject '{name}' saved as prefab to '{finalPrefabPath}' and instance connected." $"[ManageGameObject.Create] GameObject '{name}' saved as prefab to '{finalPrefabPath}' and instance connected."
); );
// Mark the new prefab asset as dirty? Not usually necessary, SaveAsPrefabAsset handles it. // Mark the new prefab asset as dirty? Not usually necessary, SaveAsPrefabAsset handles it.
@ -593,7 +593,7 @@ namespace MCPForUnity.Editor.Tools
// Check if tag exists first (Unity doesn't throw exceptions for undefined tags, just logs a warning) // Check if tag exists first (Unity doesn't throw exceptions for undefined tags, just logs a warning)
if (tagToSet != "Untagged" && !System.Linq.Enumerable.Contains(InternalEditorUtility.tags, tagToSet)) if (tagToSet != "Untagged" && !System.Linq.Enumerable.Contains(InternalEditorUtility.tags, tagToSet))
{ {
Debug.Log($"[ManageGameObject] Tag '{tagToSet}' not found. Creating it."); McpLog.Info($"[ManageGameObject] Tag '{tagToSet}' not found. Creating it.");
try try
{ {
InternalEditorUtility.AddTag(tagToSet); InternalEditorUtility.AddTag(tagToSet);
@ -850,7 +850,7 @@ namespace MCPForUnity.Editor.Tools
} }
else else
{ {
Debug.LogWarning($"[ManageGameObject.Duplicate] Parent '{parentToken}' not found. Keeping original parent."); McpLog.Warn($"[ManageGameObject.Duplicate] Parent '{parentToken}' not found. Keeping original parent.");
} }
} }
} }
@ -976,7 +976,7 @@ namespace MCPForUnity.Editor.Tools
case "forward": case "front": return Vector3.forward; case "forward": case "front": return Vector3.forward;
case "back": case "backward": case "behind": return Vector3.back; case "back": case "backward": case "behind": return Vector3.back;
default: default:
Debug.LogWarning($"[ManageGameObject.MoveRelative] Unknown direction '{direction}', defaulting to forward."); McpLog.Warn($"[ManageGameObject.MoveRelative] Unknown direction '{direction}', defaulting to forward.");
return Vector3.forward; return Vector3.forward;
} }
} }
@ -992,7 +992,7 @@ namespace MCPForUnity.Editor.Tools
case "forward": case "front": return referenceTransform.forward; case "forward": case "front": return referenceTransform.forward;
case "back": case "backward": case "behind": return -referenceTransform.forward; case "back": case "backward": case "behind": return -referenceTransform.forward;
default: default:
Debug.LogWarning($"[ManageGameObject.MoveRelative] Unknown direction '{direction}', defaulting to forward."); McpLog.Warn($"[ManageGameObject.MoveRelative] Unknown direction '{direction}', defaulting to forward.");
return referenceTransform.forward; return referenceTransform.forward;
} }
} }
@ -1057,7 +1057,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"Failed to parse JArray as Vector3: {array}. Error: {ex.Message}"); McpLog.Warn($"Failed to parse JArray as Vector3: {array}. Error: {ex.Message}");
} }
} }
return null; return null;
@ -1124,7 +1124,7 @@ namespace MCPForUnity.Editor.Tools
rootSearchObject = FindObjectInternal(targetToken, "by_id_or_name_or_path"); // Find the root for child search rootSearchObject = FindObjectInternal(targetToken, "by_id_or_name_or_path"); // Find the root for child search
if (rootSearchObject == null) if (rootSearchObject == null)
{ {
Debug.LogWarning( McpLog.Warn(
$"[ManageGameObject.Find] Root object '{targetToken}' for child search not found." $"[ManageGameObject.Find] Root object '{targetToken}' for child search not found."
); );
return results; // Return empty if root not found return results; // Return empty if root not found
@ -1209,7 +1209,7 @@ namespace MCPForUnity.Editor.Tools
} }
else else
{ {
Debug.LogWarning( McpLog.Warn(
$"[ManageGameObject.Find] Component type not found: {searchTerm}" $"[ManageGameObject.Find] Component type not found: {searchTerm}"
); );
} }
@ -1238,7 +1238,7 @@ namespace MCPForUnity.Editor.Tools
results.AddRange(allObjectsName.Where(go => go.name == searchTerm)); results.AddRange(allObjectsName.Where(go => go.name == searchTerm));
break; break;
default: default:
Debug.LogWarning( McpLog.Warn(
$"[ManageGameObject.Find] Unknown search method: {searchMethod}" $"[ManageGameObject.Find] Unknown search method: {searchMethod}"
); );
break; break;
@ -1465,13 +1465,13 @@ namespace MCPForUnity.Editor.Tools
var msg = suggestions.Any() var msg = suggestions.Any()
? $"Property '{propName}' not found. Did you mean: {string.Join(", ", suggestions)}? Available: [{string.Join(", ", availableProperties)}]" ? $"Property '{propName}' not found. Did you mean: {string.Join(", ", suggestions)}? Available: [{string.Join(", ", availableProperties)}]"
: $"Property '{propName}' not found. Available: [{string.Join(", ", availableProperties)}]"; : $"Property '{propName}' not found. Available: [{string.Join(", ", availableProperties)}]";
Debug.LogWarning($"[ManageGameObject] {msg}"); McpLog.Warn($"[ManageGameObject] {msg}");
failures.Add(msg); failures.Add(msg);
} }
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError( McpLog.Error(
$"[ManageGameObject] Error setting property '{propName}' on '{compName}': {e.Message}" $"[ManageGameObject] Error setting property '{propName}' on '{compName}': {e.Message}"
); );
failures.Add($"Error setting '{propName}': {e.Message}"); failures.Add($"Error setting '{propName}': {e.Message}");
@ -1522,7 +1522,7 @@ namespace MCPForUnity.Editor.Tools
} }
else else
{ {
Debug.LogWarning($"[SetProperty] Conversion failed for property '{memberName}' (Type: {propInfo.PropertyType.Name}) from token: {value.ToString(Formatting.None)}"); McpLog.Warn($"[SetProperty] Conversion failed for property '{memberName}' (Type: {propInfo.PropertyType.Name}) from token: {value.ToString(Formatting.None)}");
} }
} }
else else
@ -1541,7 +1541,7 @@ namespace MCPForUnity.Editor.Tools
} }
else else
{ {
Debug.LogWarning($"[SetProperty] Conversion failed for field '{memberName}' (Type: {fieldInfo.FieldType.Name}) from token: {value.ToString(Formatting.None)}"); McpLog.Warn($"[SetProperty] Conversion failed for field '{memberName}' (Type: {fieldInfo.FieldType.Name}) from token: {value.ToString(Formatting.None)}");
} }
} }
else else
@ -1563,7 +1563,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogError( McpLog.Error(
$"[SetProperty] Failed to set '{memberName}' on {type.Name}: {ex.Message}\nToken: {value.ToString(Formatting.None)}" $"[SetProperty] Failed to set '{memberName}' on {type.Name}: {ex.Message}\nToken: {value.ToString(Formatting.None)}"
); );
} }
@ -1622,7 +1622,7 @@ namespace MCPForUnity.Editor.Tools
fieldInfo = currentType.GetField(part, flags); fieldInfo = currentType.GetField(part, flags);
if (fieldInfo == null) if (fieldInfo == null)
{ {
Debug.LogWarning( McpLog.Warn(
$"[SetNestedProperty] Could not find property or field '{part}' on type '{currentType.Name}'" $"[SetNestedProperty] Could not find property or field '{part}' on type '{currentType.Name}'"
); );
return false; return false;
@ -1637,7 +1637,7 @@ namespace MCPForUnity.Editor.Tools
//Need to stop if current property is null //Need to stop if current property is null
if (currentObject == null) if (currentObject == null)
{ {
Debug.LogWarning( McpLog.Warn(
$"[SetNestedProperty] Property '{part}' is null, cannot access nested properties." $"[SetNestedProperty] Property '{part}' is null, cannot access nested properties."
); );
return false; return false;
@ -1650,7 +1650,7 @@ namespace MCPForUnity.Editor.Tools
var materials = currentObject as Material[]; var materials = currentObject as Material[];
if (arrayIndex < 0 || arrayIndex >= materials.Length) if (arrayIndex < 0 || arrayIndex >= materials.Length)
{ {
Debug.LogWarning( McpLog.Warn(
$"[SetNestedProperty] Material index {arrayIndex} out of range (0-{materials.Length - 1})" $"[SetNestedProperty] Material index {arrayIndex} out of range (0-{materials.Length - 1})"
); );
return false; return false;
@ -1662,7 +1662,7 @@ namespace MCPForUnity.Editor.Tools
var list = currentObject as System.Collections.IList; var list = currentObject as System.Collections.IList;
if (arrayIndex < 0 || arrayIndex >= list.Count) if (arrayIndex < 0 || arrayIndex >= list.Count)
{ {
Debug.LogWarning( McpLog.Warn(
$"[SetNestedProperty] Index {arrayIndex} out of range (0-{list.Count - 1})" $"[SetNestedProperty] Index {arrayIndex} out of range (0-{list.Count - 1})"
); );
return false; return false;
@ -1671,7 +1671,7 @@ namespace MCPForUnity.Editor.Tools
} }
else else
{ {
Debug.LogWarning( McpLog.Warn(
$"[SetNestedProperty] Property '{part}' is not an array or list, cannot access by index." $"[SetNestedProperty] Property '{part}' is not an array or list, cannot access by index."
); );
return false; return false;
@ -1702,7 +1702,7 @@ namespace MCPForUnity.Editor.Tools
} }
else else
{ {
Debug.LogWarning($"[SetNestedProperty] Final conversion failed for property '{finalPart}' (Type: {finalPropInfo.PropertyType.Name}) from token: {value.ToString(Formatting.None)}"); McpLog.Warn($"[SetNestedProperty] Final conversion failed for property '{finalPart}' (Type: {finalPropInfo.PropertyType.Name}) from token: {value.ToString(Formatting.None)}");
} }
} }
else else
@ -1719,12 +1719,12 @@ namespace MCPForUnity.Editor.Tools
} }
else else
{ {
Debug.LogWarning($"[SetNestedProperty] Final conversion failed for field '{finalPart}' (Type: {finalFieldInfo.FieldType.Name}) from token: {value.ToString(Formatting.None)}"); McpLog.Warn($"[SetNestedProperty] Final conversion failed for field '{finalPart}' (Type: {finalFieldInfo.FieldType.Name}) from token: {value.ToString(Formatting.None)}");
} }
} }
else else
{ {
Debug.LogWarning( McpLog.Warn(
$"[SetNestedProperty] Could not find final writable property or field '{finalPart}' on type '{currentType.Name}'" $"[SetNestedProperty] Could not find final writable property or field '{finalPart}' on type '{currentType.Name}'"
); );
} }
@ -1732,7 +1732,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogError( McpLog.Error(
$"[SetNestedProperty] Error setting nested property '{path}': {ex.Message}\nToken: {value.ToString(Formatting.None)}" $"[SetNestedProperty] Error setting nested property '{path}': {ex.Message}\nToken: {value.ToString(Formatting.None)}"
); );
} }
@ -1805,7 +1805,7 @@ namespace MCPForUnity.Editor.Tools
{ {
return new Vector3(arr[0].ToObject<float>(), arr[1].ToObject<float>(), arr[2].ToObject<float>()); return new Vector3(arr[0].ToObject<float>(), arr[1].ToObject<float>(), arr[2].ToObject<float>());
} }
Debug.LogWarning($"Could not parse JToken '{token}' as Vector3 using fallback. Returning Vector3.zero."); McpLog.Warn($"Could not parse JToken '{token}' as Vector3 using fallback. Returning Vector3.zero.");
return Vector3.zero; return Vector3.zero;
} }
@ -1820,7 +1820,7 @@ namespace MCPForUnity.Editor.Tools
{ {
return new Vector2(arr[0].ToObject<float>(), arr[1].ToObject<float>()); return new Vector2(arr[0].ToObject<float>(), arr[1].ToObject<float>());
} }
Debug.LogWarning($"Could not parse JToken '{token}' as Vector2 using fallback. Returning Vector2.zero."); McpLog.Warn($"Could not parse JToken '{token}' as Vector2 using fallback. Returning Vector2.zero.");
return Vector2.zero; return Vector2.zero;
} }
private static Quaternion ParseJTokenToQuaternion(JToken token) private static Quaternion ParseJTokenToQuaternion(JToken token)
@ -1834,7 +1834,7 @@ namespace MCPForUnity.Editor.Tools
{ {
return new Quaternion(arr[0].ToObject<float>(), arr[1].ToObject<float>(), arr[2].ToObject<float>(), arr[3].ToObject<float>()); return new Quaternion(arr[0].ToObject<float>(), arr[1].ToObject<float>(), arr[2].ToObject<float>(), arr[3].ToObject<float>());
} }
Debug.LogWarning($"Could not parse JToken '{token}' as Quaternion using fallback. Returning Quaternion.identity."); McpLog.Warn($"Could not parse JToken '{token}' as Quaternion using fallback. Returning Quaternion.identity.");
return Quaternion.identity; return Quaternion.identity;
} }
private static Color ParseJTokenToColor(JToken token) private static Color ParseJTokenToColor(JToken token)
@ -1848,7 +1848,7 @@ namespace MCPForUnity.Editor.Tools
{ {
return new Color(arr[0].ToObject<float>(), arr[1].ToObject<float>(), arr[2].ToObject<float>(), arr[3].ToObject<float>()); return new Color(arr[0].ToObject<float>(), arr[1].ToObject<float>(), arr[2].ToObject<float>(), arr[3].ToObject<float>());
} }
Debug.LogWarning($"Could not parse JToken '{token}' as Color using fallback. Returning Color.white."); McpLog.Warn($"Could not parse JToken '{token}' as Color using fallback. Returning Color.white.");
return Color.white; return Color.white;
} }
private static Rect ParseJTokenToRect(JToken token) private static Rect ParseJTokenToRect(JToken token)
@ -1862,7 +1862,7 @@ namespace MCPForUnity.Editor.Tools
{ {
return new Rect(arr[0].ToObject<float>(), arr[1].ToObject<float>(), arr[2].ToObject<float>(), arr[3].ToObject<float>()); return new Rect(arr[0].ToObject<float>(), arr[1].ToObject<float>(), arr[2].ToObject<float>(), arr[3].ToObject<float>());
} }
Debug.LogWarning($"Could not parse JToken '{token}' as Rect using fallback. Returning Rect.zero."); McpLog.Warn($"Could not parse JToken '{token}' as Rect using fallback. Returning Rect.zero.");
return Rect.zero; return Rect.zero;
} }
private static Bounds ParseJTokenToBounds(JToken token) private static Bounds ParseJTokenToBounds(JToken token)
@ -1880,7 +1880,7 @@ namespace MCPForUnity.Editor.Tools
// { // {
// return new Bounds(new Vector3(arr[0].ToObject<float>(), arr[1].ToObject<float>(), arr[2].ToObject<float>()), new Vector3(arr[3].ToObject<float>(), arr[4].ToObject<float>(), arr[5].ToObject<float>())); // return new Bounds(new Vector3(arr[0].ToObject<float>(), arr[1].ToObject<float>(), arr[2].ToObject<float>()), new Vector3(arr[3].ToObject<float>(), arr[4].ToObject<float>(), arr[5].ToObject<float>()));
// } // }
Debug.LogWarning($"Could not parse JToken '{token}' as Bounds using fallback. Returning new Bounds(Vector3.zero, Vector3.zero)."); McpLog.Warn($"Could not parse JToken '{token}' as Bounds using fallback. Returning new Bounds(Vector3.zero, Vector3.zero).");
return new Bounds(Vector3.zero, Vector3.zero); return new Bounds(Vector3.zero, Vector3.zero);
} }
// --- End Redundant Parse Helpers --- // --- End Redundant Parse Helpers ---
@ -1913,7 +1913,7 @@ namespace MCPForUnity.Editor.Tools
// Log the resolver error if type wasn't found // Log the resolver error if type wasn't found
if (!string.IsNullOrEmpty(error)) if (!string.IsNullOrEmpty(error))
{ {
Debug.LogWarning($"[FindType] {error}"); McpLog.Warn($"[FindType] {error}");
} }
return null; return null;
@ -1980,7 +1980,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"[Property Matching] Error getting suggestions for '{userInput}': {ex.Message}"); McpLog.Warn($"[Property Matching] Error getting suggestions for '{userInput}': {ex.Message}");
return new List<string>(); return new List<string>();
} }
} }

View File

@ -263,7 +263,7 @@ namespace MCPForUnity.Editor.Tools
: new ErrorResponse("Validation failed.", result); : new ErrorResponse("Validation failed.", result);
} }
case "edit": case "edit":
Debug.LogWarning("manage_script.edit is deprecated; prefer apply_text_edits. Serving structured edit for backward compatibility."); McpLog.Warn("manage_script.edit is deprecated; prefer apply_text_edits. Serving structured edit for backward compatibility.");
var structEdits = @params["edits"] as JArray; var structEdits = @params["edits"] as JArray;
var options = @params["options"] as JObject; var options = @params["options"] as JObject;
return EditScript(fullPath, relativePath, name, structEdits, options); return EditScript(fullPath, relativePath, name, structEdits, options);
@ -353,7 +353,7 @@ namespace MCPForUnity.Editor.Tools
else if (validationErrors != null && validationErrors.Length > 0) else if (validationErrors != null && validationErrors.Length > 0)
{ {
// Log warnings but don't block creation // Log warnings but don't block creation
Debug.LogWarning($"Script validation warnings for {name}:\n" + string.Join("\n", validationErrors)); McpLog.Warn($"Script validation warnings for {name}:\n" + string.Join("\n", validationErrors));
} }
try try
@ -451,7 +451,7 @@ namespace MCPForUnity.Editor.Tools
else if (validationErrors != null && validationErrors.Length > 0) else if (validationErrors != null && validationErrors.Length > 0)
{ {
// Log warnings but don't block update // Log warnings but don't block update
Debug.LogWarning($"Script validation warnings for {name}:\n" + string.Join("\n", validationErrors)); McpLog.Warn($"Script validation warnings for {name}:\n" + string.Join("\n", validationErrors));
} }
try try
@ -1405,7 +1405,7 @@ namespace MCPForUnity.Editor.Tools
if (!ValidateScriptSyntax(working, level, out var errors)) if (!ValidateScriptSyntax(working, level, out var errors))
return new ErrorResponse("validation_failed", new { status = "validation_failed", diagnostics = errors ?? Array.Empty<string>() }); return new ErrorResponse("validation_failed", new { status = "validation_failed", diagnostics = errors ?? Array.Empty<string>() });
else if (errors != null && errors.Length > 0) else if (errors != null && errors.Length > 0)
Debug.LogWarning($"Script validation warnings for {name}:\n" + string.Join("\n", errors)); McpLog.Warn($"Script validation warnings for {name}:\n" + string.Join("\n", errors));
// Atomic write with backup; schedule refresh // Atomic write with backup; schedule refresh
// Decide refresh behavior // Decide refresh behavior
@ -2310,7 +2310,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"Could not load UnityEngine assembly: {ex.Message}"); McpLog.Warn($"Could not load UnityEngine assembly: {ex.Message}");
} }
#if UNITY_EDITOR #if UNITY_EDITOR
@ -2320,7 +2320,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"Could not load UnityEditor assembly: {ex.Message}"); McpLog.Warn($"Could not load UnityEditor assembly: {ex.Message}");
} }
// Get Unity project assemblies // Get Unity project assemblies
@ -2337,7 +2337,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogWarning($"Could not load Unity project assemblies: {ex.Message}"); McpLog.Warn($"Could not load Unity project assemblies: {ex.Message}");
} }
#endif #endif
@ -2349,7 +2349,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception ex) catch (Exception ex)
{ {
Debug.LogError($"Failed to get compilation references: {ex.Message}"); McpLog.Error($"Failed to get compilation references: {ex.Message}");
return new System.Collections.Generic.List<MetadataReference>(); return new System.Collections.Generic.List<MetadataReference>();
} }
} }

View File

@ -597,7 +597,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogWarning($"[MCP] Could not parse '{paramName}' JSON string: {e.Message}"); McpLog.Warn($"[MCP] Could not parse '{paramName}' JSON string: {e.Message}");
} }
} }
} }

View File

@ -111,7 +111,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError( McpLog.Error(
$"[ReadConsole] Static Initialization Failed: Could not setup reflection for LogEntries/LogEntry. Console reading/clearing will likely fail. Specific Error: {e.Message}" $"[ReadConsole] Static Initialization Failed: Could not setup reflection for LogEntries/LogEntry. Console reading/clearing will likely fail. Specific Error: {e.Message}"
); );
// Set members to null to prevent NullReferenceExceptions later, HandleCommand should check this. // Set members to null to prevent NullReferenceExceptions later, HandleCommand should check this.
@ -144,7 +144,7 @@ namespace MCPForUnity.Editor.Tools
) )
{ {
// Log the error here as well for easier debugging in Unity Console // Log the error here as well for easier debugging in Unity Console
Debug.LogError( McpLog.Error(
"[ReadConsole] HandleCommand called but reflection members are not initialized. Static constructor might have failed silently or there's an issue." "[ReadConsole] HandleCommand called but reflection members are not initialized. Static constructor might have failed silently or there's an issue."
); );
return new ErrorResponse( return new ErrorResponse(
@ -184,7 +184,7 @@ namespace MCPForUnity.Editor.Tools
if (!string.IsNullOrEmpty(sinceTimestampStr)) if (!string.IsNullOrEmpty(sinceTimestampStr))
{ {
Debug.LogWarning( McpLog.Warn(
"[ReadConsole] Filtering by 'since_timestamp' is not currently implemented." "[ReadConsole] Filtering by 'since_timestamp' is not currently implemented."
); );
// Need a way to get timestamp per log entry. // Need a way to get timestamp per log entry.
@ -209,7 +209,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError($"[ReadConsole] Action '{action}' failed: {e}"); McpLog.Error($"[ReadConsole] Action '{action}' failed: {e}");
return new ErrorResponse($"Internal error processing action '{action}': {e.Message}"); return new ErrorResponse($"Internal error processing action '{action}': {e.Message}");
} }
} }
@ -225,7 +225,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError($"[ReadConsole] Failed to clear console: {e}"); McpLog.Error($"[ReadConsole] Failed to clear console: {e}");
return new ErrorResponse($"Failed to clear console: {e.Message}"); return new ErrorResponse($"Failed to clear console: {e.Message}");
} }
} }
@ -400,7 +400,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError($"[ReadConsole] Error while retrieving log entries: {e}"); McpLog.Error($"[ReadConsole] Error while retrieving log entries: {e}");
// EndGettingEntries will be called in the finally block // EndGettingEntries will be called in the finally block
return new ErrorResponse($"Error retrieving log entries: {e.Message}"); return new ErrorResponse($"Error retrieving log entries: {e.Message}");
} }
@ -413,7 +413,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception e) catch (Exception e)
{ {
Debug.LogError($"[ReadConsole] Failed to call EndGettingEntries: {e}"); McpLog.Error($"[ReadConsole] Failed to call EndGettingEntries: {e}");
// Don't return error here as we might have valid data, but log it. // Don't return error here as we might have valid data, but log it.
} }
} }

View File

@ -110,6 +110,14 @@ namespace MCPForUnity.Editor.Windows.Components.Connection
if (string.IsNullOrEmpty(scope)) if (string.IsNullOrEmpty(scope))
{ {
scope = MCPServiceLocator.Server.IsLocalUrl() ? "local" : "remote"; scope = MCPServiceLocator.Server.IsLocalUrl() ? "local" : "remote";
try
{
EditorPrefs.SetString(EditorPrefKeys.HttpTransportScope, scope);
}
catch
{
McpLog.Debug("Failed to set HttpTransportScope pref.");
}
} }
transportDropdown.value = scope == "remote" ? TransportProtocol.HTTPRemote : TransportProtocol.HTTPLocal; transportDropdown.value = scope == "remote" ? TransportProtocol.HTTPRemote : TransportProtocol.HTTPLocal;

View File

@ -87,7 +87,7 @@ namespace MCPForUnity.Editor.Windows
if (visualTree == null) if (visualTree == null)
{ {
Debug.LogError("Failed to load EditorPrefsWindow.uxml template"); McpLog.Error("Failed to load EditorPrefsWindow.uxml template");
return; return;
} }
@ -98,7 +98,7 @@ namespace MCPForUnity.Editor.Windows
if (itemTemplate == null) if (itemTemplate == null)
{ {
Debug.LogError("Failed to load EditorPrefItem.uxml template"); McpLog.Error("Failed to load EditorPrefItem.uxml template");
return; return;
} }
@ -251,7 +251,7 @@ namespace MCPForUnity.Editor.Windows
{ {
if (itemTemplate == null) if (itemTemplate == null)
{ {
Debug.LogError("Item template not loaded"); McpLog.Error("Item template not loaded");
return new VisualElement(); return new VisualElement();
} }

View File

@ -341,7 +341,7 @@ namespace MCPForUnity.Runtime.Serialization
#else #else
// Runtime deserialization is tricky without AssetDatabase/EditorUtility // Runtime deserialization is tricky without AssetDatabase/EditorUtility
// Maybe log a warning and return null or existingValue? // Maybe log a warning and return null or existingValue?
Debug.LogWarning("UnityEngineObjectConverter cannot deserialize complex objects in non-Editor mode."); McpLog.Warn("UnityEngineObjectConverter cannot deserialize complex objects in non-Editor mode.");
// Skip the token to avoid breaking the reader // Skip the token to avoid breaking the reader
if (reader.TokenType == JsonToken.StartObject) JObject.Load(reader); if (reader.TokenType == JsonToken.StartObject) JObject.Load(reader);
else if (reader.TokenType == JsonToken.String) reader.ReadAsString(); else if (reader.TokenType == JsonToken.String) reader.ReadAsString();

View File

@ -97,8 +97,9 @@ MCP for Unity 使用两个组件连接您的工具:
### 前置要求 ### 前置要求
如果你**不是**通过 Unity Asset Store 安装,则还需要安装以下内容:
* **Python** 版本 3.10 或更新。[下载 Python](https://www.python.org/downloads/) * **Python** 版本 3.10 或更新。[下载 Python](https://www.python.org/downloads/)
* **Unity Hub 和编辑器:** 版本 2021.3 LTS 或更新。[下载 Unity](https://unity.com/download)
* **uvPython 工具链管理器):** * **uvPython 工具链管理器):**
```bash ```bash
# macOS / Linux # macOS / Linux
@ -109,34 +110,44 @@ MCP for Unity 使用两个组件连接您的工具:
# 文档: https://docs.astral.sh/uv/getting-started/installation/ # 文档: https://docs.astral.sh/uv/getting-started/installation/
``` ```
所有安装方式都需要以下内容:
* **Unity Hub 和编辑器:** 版本 2021.3 LTS 或更新。[下载 Unity](https://unity.com/download)
* **MCP 客户端:** [Claude Desktop](https://claude.ai/download) | [Claude Code](https://github.com/anthropics/claude-code) | [Cursor](https://www.cursor.com/en/downloads) | [Visual Studio Code Copilot](https://code.visualstudio.com/docs/copilot/overview) | [Windsurf](https://windsurf.com) | 其他客户端可通过手动配置使用 * **MCP 客户端:** [Claude Desktop](https://claude.ai/download) | [Claude Code](https://github.com/anthropics/claude-code) | [Cursor](https://www.cursor.com/en/downloads) | [Visual Studio Code Copilot](https://code.visualstudio.com/docs/copilot/overview) | [Windsurf](https://windsurf.com) | 其他客户端可通过手动配置使用
* <details> <summary><strong>[可选] Roslyn 用于高级脚本验证</strong></summary> <details> <summary><strong>[可选] Roslyn 用于高级脚本验证</strong></summary>
对于捕获未定义命名空间、类型和方法的**严格**验证级别: 对于捕获未定义命名空间、类型和方法的**严格**验证级别:
**方法 1Unity 的 NuGet推荐** **方法 1Unity 的 NuGet推荐**
1. 安装 [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity) 1. 安装 [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity)
2. 前往 `Window > NuGet Package Manager` 2. 前往 `Window > NuGet Package Manager`
3. 搜索 `Microsoft.CodeAnalysis`,选择版本 4.14.0 并安装包 3. 搜索 `Microsoft.CodeAnalysis`,选择版本 4.14.0 并安装包
4. 同时安装包 `SQLitePCLRaw.core``SQLitePCLRaw.bundle_e_sqlite3` 4. 同时安装包 `SQLitePCLRaw.core``SQLitePCLRaw.bundle_e_sqlite3`
5. 前往 `Player Settings > Scripting Define Symbols` 5. 前往 `Player Settings > Scripting Define Symbols`
6. 添加 `USE_ROSLYN` 6. 添加 `USE_ROSLYN`
7. 重启 Unity 7. 重启 Unity
**方法 2手动 DLL 安装** **方法 2手动 DLL 安装**
1. 从 [NuGet](https://www.nuget.org/packages/Microsoft.CodeAnalysis.CSharp/) 下载 Microsoft.CodeAnalysis.CSharp.dll 和依赖项 1. 从 [NuGet](https://www.nuget.org/packages/Microsoft.CodeAnalysis.CSharp/) 下载 Microsoft.CodeAnalysis.CSharp.dll 和依赖项
2. 将 DLL 放置在 `Assets/Plugins/` 文件夹中 2. 将 DLL 放置在 `Assets/Plugins/` 文件夹中
3. 确保 .NET 兼容性设置正确 3. 确保 .NET 兼容性设置正确
4. 将 `USE_ROSLYN` 添加到脚本定义符号 4. 将 `USE_ROSLYN` 添加到脚本定义符号
5. 重启 Unity 5. 重启 Unity
**注意:** 没有 Roslyn 时脚本验证会回退到基本结构检查。Roslyn 启用完整的 C# 编译器诊断和精确错误报告。</details> **注意:** 没有 Roslyn 时脚本验证会回退到基本结构检查。Roslyn 启用完整的 C# 编译器诊断和精确错误报告。</details>
--- ---
### 🌟 步骤 1安装 Unity 包 ### 🌟 步骤 1安装 Unity 包
#### 通过 Unity Asset Store 安装
1. 在浏览器中打开https://assetstore.unity.com/packages/tools/generative-ai/mcp-for-unity-ai-driven-development-329908
2. 点击 `Add to My Assets`
3. 在 Unity 编辑器中,前往 `Window > Package Manager`
4. 将该资源下载并导入到你的项目中
#### 通过 Git URL 安装 #### 通过 Git URL 安装
1. 打开您的 Unity 项目。 1. 打开您的 Unity 项目。
@ -150,7 +161,7 @@ MCP for Unity 使用两个组件连接您的工具:
**需要锁定版本?** 使用带标签的 URL更新时需卸载并重新安装 **需要锁定版本?** 使用带标签的 URL更新时需卸载并重新安装
``` ```
https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#v8.0.0 https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#v8.6.0
``` ```
#### 通过 OpenUPM 安装 #### 通过 OpenUPM 安装

View File

@ -8,6 +8,7 @@
[![Discord](https://img.shields.io/badge/discord-join-red.svg?logo=discord&logoColor=white)](https://discord.gg/y4p8KfzrN4) [![Discord](https://img.shields.io/badge/discord-join-red.svg?logo=discord&logoColor=white)](https://discord.gg/y4p8KfzrN4)
[![](https://img.shields.io/badge/Website-Visit-purple)](https://www.coplay.dev/?ref=unity-mcp) [![](https://img.shields.io/badge/Website-Visit-purple)](https://www.coplay.dev/?ref=unity-mcp)
[![](https://img.shields.io/badge/Unity-000000?style=flat&logo=unity&logoColor=blue 'Unity')](https://unity.com/releases/editor/archive) [![](https://img.shields.io/badge/Unity-000000?style=flat&logo=unity&logoColor=blue 'Unity')](https://unity.com/releases/editor/archive)
[![Unity Asset Store](https://img.shields.io/badge/Unity%20Asset%20Store-Get%20Package-FF6A00?style=flat&logo=unity&logoColor=white)](https://assetstore.unity.com/packages/tools/generative-ai/mcp-for-unity-ai-driven-development-329908)
[![python](https://img.shields.io/badge/Python-3.10+-3776AB.svg?style=flat&logo=python&logoColor=white)](https://www.python.org) [![python](https://img.shields.io/badge/Python-3.10+-3776AB.svg?style=flat&logo=python&logoColor=white)](https://www.python.org)
[![](https://badge.mcpx.dev?status=on 'MCP Enabled')](https://modelcontextprotocol.io/introduction) [![](https://badge.mcpx.dev?status=on 'MCP Enabled')](https://modelcontextprotocol.io/introduction)
![GitHub commit activity](https://img.shields.io/github/commit-activity/w/CoplayDev/unity-mcp) ![GitHub commit activity](https://img.shields.io/github/commit-activity/w/CoplayDev/unity-mcp)
@ -18,7 +19,7 @@
MCP for Unity acts as a bridge, allowing AI assistants (Claude, Cursor, Antigravity, VS Code, etc) to interact directly with your Unity Editor via a local **MCP (Model Context Protocol) Client**. Give your LLM tools to manage assets, control scenes, edit scripts, and automate tasks within Unity. MCP for Unity acts as a bridge, allowing AI assistants (Claude, Cursor, Antigravity, VS Code, etc) to interact directly with your Unity Editor via a local **MCP (Model Context Protocol) Client**. Give your LLM tools to manage assets, control scenes, edit scripts, and automate tasks within Unity.
<img width="406" height="704" alt="MCP for Unity screenshot" src="docs/images/unity-mcp-ui-v8.6.png"> <img alt="MCP for Unity building a scene" src="docs/images/building_scene.gif">
--- ---
@ -110,8 +111,9 @@ MCP for Unity connects your tools using two components:
### Prerequisites ### Prerequisites
If you are **not** installing via the Unity Asset Store, you will need to install the following:
* **Python:** Version 3.10 or newer. [Download Python](https://www.python.org/downloads/) * **Python:** Version 3.10 or newer. [Download Python](https://www.python.org/downloads/)
* **Unity Hub & Editor:** Version 2021.3 LTS or newer. [Download Unity](https://unity.com/download)
* **uv (Python toolchain manager):** * **uv (Python toolchain manager):**
```bash ```bash
# macOS / Linux # macOS / Linux
@ -122,34 +124,45 @@ MCP for Unity connects your tools using two components:
# Docs: https://docs.astral.sh/uv/getting-started/installation/ # Docs: https://docs.astral.sh/uv/getting-started/installation/
``` ```
All installations require these:
* **Unity Hub & Editor:** Version 2021.3 LTS or newer. [Download Unity](https://unity.com/download)
* **An MCP Client:** : [Claude Desktop](https://claude.ai/download) | [Claude Code](https://github.com/anthropics/claude-code) | [Cursor](https://www.cursor.com/en/downloads) | [Visual Studio Code Copilot](https://code.visualstudio.com/docs/copilot/overview) | [Windsurf](https://windsurf.com) | Others work with manual config * **An MCP Client:** : [Claude Desktop](https://claude.ai/download) | [Claude Code](https://github.com/anthropics/claude-code) | [Cursor](https://www.cursor.com/en/downloads) | [Visual Studio Code Copilot](https://code.visualstudio.com/docs/copilot/overview) | [Windsurf](https://windsurf.com) | Others work with manual config
* <details> <summary><strong>[Optional] Roslyn for Advanced Script Validation</strong></summary> <details> <summary><strong>[Optional] Roslyn for Advanced Script Validation</strong></summary>
For **Strict** validation level that catches undefined namespaces, types, and methods: For **Strict** validation level that catches undefined namespaces, types, and methods:
**Method 1: NuGet for Unity (Recommended)** **Method 1: NuGet for Unity (Recommended)**
1. Install [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity) 1. Install [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity)
2. Go to `Window > NuGet Package Manager` 2. Go to `Window > NuGet Package Manager`
3. Search for `Microsoft.CodeAnalysis`, select version 4.14.0, and install the package 3. Search for `Microsoft.CodeAnalysis`, select version 4.14.0, and install the package
4. Also install package `SQLitePCLRaw.core` and `SQLitePCLRaw.bundle_e_sqlite3`. 4. Also install package `SQLitePCLRaw.core` and `SQLitePCLRaw.bundle_e_sqlite3`.
5. Go to `Player Settings > Scripting Define Symbols` 5. Go to `Player Settings > Scripting Define Symbols`
6. Add `USE_ROSLYN` 6. Add `USE_ROSLYN`
7. Restart Unity 7. Restart Unity
**Method 2: Manual DLL Installation** **Method 2: Manual DLL Installation**
1. Download Microsoft.CodeAnalysis.CSharp.dll and dependencies from [NuGet](https://www.nuget.org/packages/Microsoft.CodeAnalysis.CSharp/) 1. Download Microsoft.CodeAnalysis.CSharp.dll and dependencies from [NuGet](https://www.nuget.org/packages/Microsoft.CodeAnalysis.CSharp/)
2. Place DLLs in `Assets/Plugins/` folder 2. Place DLLs in `Assets/Plugins/` folder
3. Ensure .NET compatibility settings are correct 3. Ensure .NET compatibility settings are correct
4. Add `USE_ROSLYN` to Scripting Define Symbols 4. Add `USE_ROSLYN` to Scripting Define Symbols
5. Restart Unity 5. Restart Unity
**Note:** Without Roslyn, script validation falls back to basic structural checks. Roslyn enables full C# compiler diagnostics with precise error reporting.</details> **Note:** Without Roslyn, script validation falls back to basic structural checks. Roslyn enables full C# compiler diagnostics with precise error reporting.</details>
--- ---
### 🌟 Step 1: Install the Unity Package ### 🌟 Step 1: Install the Unity Package
#### To install via the Unity Asset Store
1. In your browser, navigate to https://assetstore.unity.com/packages/tools/generative-ai/mcp-for-unity-ai-driven-development-329908
2. Click `Add to My Assets`.
3. In the Unity Editor, go to`Window > Package Manager`.
4. Download and import the asset to your project
#### To install via Git URL #### To install via Git URL
1. Open your Unity project. 1. Open your Unity project.

View File

@ -0,0 +1,106 @@
# This .gitignore file should be placed at the root of your Unity project directory
#
# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore
#
.utmp/
/[Ll]ibrary/
/[Tt]emp/
/[Oo]bj/
/[Bb]uild/
/[Bb]uilds/
/[Ll]ogs/
/[Uu]ser[Ss]ettings/
*.log
# By default unity supports Blender asset imports, *.blend1 blender files do not need to be commited to version control.
*.blend1
*.blend1.meta
# MemoryCaptures can get excessive in size.
# They also could contain extremely sensitive data
/[Mm]emoryCaptures/
# Recordings can get excessive in size
/[Rr]ecordings/
# Uncomment this line if you wish to ignore the asset store tools plugin
# /[Aa]ssets/AssetStoreTools*
# Autogenerated Jetbrains Rider plugin
/[Aa]ssets/Plugins/Editor/JetBrains*
# Jetbrains Rider personal-layer settings
*.DotSettings.user
# Visual Studio cache directory
.vs/
# Gradle cache directory
.gradle/
# Autogenerated VS/MD/Consulo solution and project files
ExportedObj/
.consulo/
*.csproj
*.unityproj
*.sln
*.suo
*.tmp
*.user
*.userprefs
*.pidb
*.booproj
*.svd
*.pdb
*.mdb
*.opendb
*.VC.db
# Unity3D generated meta files
*.pidb.meta
*.pdb.meta
*.mdb.meta
# Unity3D generated file on crash reports
sysinfo.txt
# Mono auto generated files
mono_crash.*
# Builds
*.apk
*.aab
*.unitypackage
*.unitypackage.meta
*.app
# Crashlytics generated file
crashlytics-build.properties
# TestRunner generated files
InitTestScene*.unity*
# Addressables default ignores, before user customizations
/ServerData
/[Aa]ssets/StreamingAssets/aa*
/[Aa]ssets/AddressableAssetsData/link.xml*
/[Aa]ssets/Addressables_Temp*
# By default, Addressables content builds will generate addressables_content_state.bin
# files in platform-specific subfolders, for example:
# /Assets/AddressableAssetsData/OSX/addressables_content_state.bin
/[Aa]ssets/AddressableAssetsData/*/*.bin*
# Visual Scripting auto-generated files
/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Flow/UnitOptions.db
/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Flow/UnitOptions.db.meta
/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Core/Property Providers
/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Core/Property Providers.meta
# Auto-generated scenes by play mode tests
/[Aa]ssets/[Ii]nit[Tt]est[Ss]cene*.unity*
.vscode
.cursor
.windsurf
.claude
.DS_Store
boot.config

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 93766a50487224f02b29aae42971e08b guid: 8105016687592461f977c054a80ce2f2
NativeFormatImporter: NativeFormatImporter:
externalObjects: {} externalObjects: {}
mainObjectFileID: 0 mainObjectFileID: 0

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f5537d31886f841d58d487c5db45aaa0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,407 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
m_ObjectHideFlags: 0
serializedVersion: 2
m_OcclusionBakeSettings:
smallestOccluder: 5
smallestHole: 0.25
backfaceThreshold: 100
m_SceneGUID: 00000000000000000000000000000000
m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:
m_ObjectHideFlags: 0
serializedVersion: 9
m_Fog: 0
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
m_FogMode: 3
m_FogDensity: 0.01
m_LinearFogStart: 0
m_LinearFogEnd: 300
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
m_AmbientIntensity: 1
m_AmbientMode: 0
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
m_HaloStrength: 0.5
m_FlareStrength: 1
m_FlareFadeSpeed: 3
m_HaloTexture: {fileID: 0}
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
m_DefaultReflectionMode: 0
m_DefaultReflectionResolution: 128
m_ReflectionBounces: 1
m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0.18028305, g: 0.22571313, b: 0.3069213, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 12
m_GIWorkflowMode: 1
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 0
m_LightmapEditorSettings:
serializedVersion: 12
m_Resolution: 2
m_BakeResolution: 40
m_AtlasSize: 1024
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAmbientOcclusion: 0
m_Padding: 2
m_LightmapParameters: {fileID: 0}
m_LightmapsBakeMode: 1
m_TextureCompression: 1
m_FinalGather: 0
m_FinalGatherFiltering: 1
m_FinalGatherRayCount: 256
m_ReflectionCompression: 2
m_MixedBakeMode: 2
m_BakeBackend: 1
m_PVRSampling: 1
m_PVRDirectSampleCount: 32
m_PVRSampleCount: 512
m_PVRBounces: 2
m_PVREnvironmentSampleCount: 256
m_PVREnvironmentReferencePointCount: 2048
m_PVRFilteringMode: 1
m_PVRDenoiserTypeDirect: 1
m_PVRDenoiserTypeIndirect: 1
m_PVRDenoiserTypeAO: 1
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVREnvironmentMIS: 1
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 0}
m_LightingSettings: {fileID: 0}
--- !u!196 &4
NavMeshSettings:
serializedVersion: 2
m_ObjectHideFlags: 0
m_BuildSettings:
serializedVersion: 2
agentTypeID: 0
agentRadius: 0.5
agentHeight: 2
agentSlope: 45
agentClimb: 0.4
ledgeDropHeight: 0
maxJumpAcrossDistance: 0
minRegionArea: 2
manualCellSize: 0
cellSize: 0.16666667
manualTileSize: 0
tileSize: 256
accuratePlacement: 0
maxJobWorkers: 0
preserveTilesOutsideBounds: 0
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &330585543
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 330585546}
- component: {fileID: 330585545}
- component: {fileID: 330585544}
- component: {fileID: 330585547}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!81 &330585544
AudioListener:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 330585543}
m_Enabled: 1
--- !u!20 &330585545
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 330585543}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 1
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_FocalLength: 50
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 0.3
far clip plane: 1000
field of view: 60
orthographic: 0
orthographic size: 5
m_Depth: -1
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 1
m_AllowMSAA: 1
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!4 &330585546
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 330585543}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 1, z: -10}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &330585547
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 330585543}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
m_Name:
m_EditorClassIdentifier:
m_RenderShadows: 1
m_RequiresDepthTextureOption: 2
m_RequiresOpaqueTextureOption: 2
m_CameraType: 0
m_Cameras: []
m_RendererIndex: -1
m_VolumeLayerMask:
serializedVersion: 2
m_Bits: 1
m_VolumeTrigger: {fileID: 0}
m_VolumeFrameworkUpdateModeOption: 2
m_RenderPostProcessing: 1
m_Antialiasing: 0
m_AntialiasingQuality: 2
m_StopNaN: 0
m_Dithering: 0
m_ClearDepth: 1
m_AllowXRRendering: 1
m_RequiresDepthTexture: 0
m_RequiresColorTexture: 0
m_Version: 2
--- !u!1 &410087039
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 410087041}
- component: {fileID: 410087040}
- component: {fileID: 410087042}
m_Layer: 0
m_Name: Directional Light
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!108 &410087040
Light:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 410087039}
m_Enabled: 1
serializedVersion: 10
m_Type: 1
m_Shape: 0
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_Intensity: 2
m_Range: 10
m_SpotAngle: 30
m_InnerSpotAngle: 21.80208
m_CookieSize: 10
m_Shadows:
m_Type: 2
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1
m_Bias: 0.05
m_NormalBias: 0.4
m_NearPlane: 0.2
m_CullingMatrixOverride:
e00: 1
e01: 0
e02: 0
e03: 0
e10: 0
e11: 1
e12: 0
e13: 0
e20: 0
e21: 0
e22: 1
e23: 0
e30: 0
e31: 0
e32: 0
e33: 1
m_UseCullingMatrixOverride: 0
m_Cookie: {fileID: 0}
m_DrawHalo: 0
m_Flare: {fileID: 0}
m_RenderMode: 0
m_CullingMask:
serializedVersion: 2
m_Bits: 4294967295
m_RenderingLayerMask: 1
m_Lightmapping: 4
m_LightShadowCasterMode: 0
m_AreaSize: {x: 1, y: 1}
m_BounceIntensity: 1
m_ColorTemperature: 5000
m_UseColorTemperature: 1
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
m_UseBoundingSphereOverride: 0
m_UseViewFrustumForShadowCasterCull: 1
m_ShadowRadius: 0
m_ShadowAngle: 0
--- !u!4 &410087041
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 410087039}
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
m_LocalPosition: {x: 0, y: 3, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
--- !u!114 &410087042
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 410087039}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Version: 1
m_UsePipelineSettings: 1
m_AdditionalLightsShadowResolutionTier: 2
m_LightLayerMask: 1
m_CustomShadowLayers: 0
m_ShadowLayerMask: 1
m_LightCookieSize: {x: 1, y: 1}
m_LightCookieOffset: {x: 0, y: 0}
--- !u!1 &832575517
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 832575519}
- component: {fileID: 832575518}
m_Layer: 0
m_Name: Global Volume
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &832575518
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 832575517}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 172515602e62fb746b5d573b38a5fe58, type: 3}
m_Name:
m_EditorClassIdentifier:
m_IsGlobal: 1
priority: 0
blendDistance: 0
weight: 1
sharedProfile: {fileID: 11400000, guid: a6560a915ef98420e9faacc1c7438823, type: 2}
--- !u!4 &832575519
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 832575517}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 99c9720ab356a0642a771bea13969a05
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 709f11a7f3c4041caa4ef136ea32d874
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a6560a915ef98420e9faacc1c7438823
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e634585d5c4544dd297acaee93dc2beb
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e1260c1148f6143b28bae5ace5e9c5d1
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c40be3174f62c4acf8c1216858c64956
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7b7fd9122c28c4d15b667c7040e3b3fd
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 707360a9c581a4bd7aa53bfeb1429f71
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d0e2fc18fe036412f8223b3b3d9ad574
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ba062aa6c92b140379dbc06b43dd3b9b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 8a0c9218a650547d98138cd835033977
folderAsset: yes
timeCreated: 1484670163
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,134 @@
fileFormatVersion: 2
guid: 727a75301c3d24613a3ebcec4a24c2c8
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMasterTextureLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 0
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: iPhone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,654 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &1
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12004, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_PixelRect:
serializedVersion: 2
x: 0
y: 45
width: 1666
height: 958
m_ShowMode: 4
m_Title:
m_RootView: {fileID: 6}
m_MinSize: {x: 950, y: 542}
m_MaxSize: {x: 10000, y: 10000}
--- !u!114 &2
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_Children: []
m_Position:
serializedVersion: 2
x: 0
y: 466
width: 290
height: 442
m_MinSize: {x: 234, y: 271}
m_MaxSize: {x: 10004, y: 10021}
m_ActualView: {fileID: 14}
m_Panes:
- {fileID: 14}
m_Selected: 0
m_LastSelected: 0
--- !u!114 &3
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_Children:
- {fileID: 4}
- {fileID: 2}
m_Position:
serializedVersion: 2
x: 973
y: 0
width: 290
height: 908
m_MinSize: {x: 234, y: 492}
m_MaxSize: {x: 10004, y: 14042}
vertical: 1
controlID: 226
--- !u!114 &4
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_Children: []
m_Position:
serializedVersion: 2
x: 0
y: 0
width: 290
height: 466
m_MinSize: {x: 204, y: 221}
m_MaxSize: {x: 4004, y: 4021}
m_ActualView: {fileID: 17}
m_Panes:
- {fileID: 17}
m_Selected: 0
m_LastSelected: 0
--- !u!114 &5
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_Children: []
m_Position:
serializedVersion: 2
x: 0
y: 466
width: 973
height: 442
m_MinSize: {x: 202, y: 221}
m_MaxSize: {x: 4002, y: 4021}
m_ActualView: {fileID: 15}
m_Panes:
- {fileID: 15}
m_Selected: 0
m_LastSelected: 0
--- !u!114 &6
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12008, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_Children:
- {fileID: 7}
- {fileID: 8}
- {fileID: 9}
m_Position:
serializedVersion: 2
x: 0
y: 0
width: 1666
height: 958
m_MinSize: {x: 950, y: 542}
m_MaxSize: {x: 10000, y: 10000}
--- !u!114 &7
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12011, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_Children: []
m_Position:
serializedVersion: 2
x: 0
y: 0
width: 1666
height: 30
m_MinSize: {x: 0, y: 0}
m_MaxSize: {x: 0, y: 0}
m_LastLoadedLayoutName: Tutorial
--- !u!114 &8
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_Children:
- {fileID: 10}
- {fileID: 3}
- {fileID: 11}
m_Position:
serializedVersion: 2
x: 0
y: 30
width: 1666
height: 908
m_MinSize: {x: 713, y: 492}
m_MaxSize: {x: 18008, y: 14042}
vertical: 0
controlID: 74
--- !u!114 &9
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12042, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_Children: []
m_Position:
serializedVersion: 2
x: 0
y: 938
width: 1666
height: 20
m_MinSize: {x: 0, y: 0}
m_MaxSize: {x: 0, y: 0}
--- !u!114 &10
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_Children:
- {fileID: 12}
- {fileID: 5}
m_Position:
serializedVersion: 2
x: 0
y: 0
width: 973
height: 908
m_MinSize: {x: 202, y: 442}
m_MaxSize: {x: 4002, y: 8042}
vertical: 1
controlID: 75
--- !u!114 &11
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_Children: []
m_Position:
serializedVersion: 2
x: 1263
y: 0
width: 403
height: 908
m_MinSize: {x: 277, y: 71}
m_MaxSize: {x: 4002, y: 4021}
m_ActualView: {fileID: 13}
m_Panes:
- {fileID: 13}
m_Selected: 0
m_LastSelected: 0
--- !u!114 &12
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_Children: []
m_Position:
serializedVersion: 2
x: 0
y: 0
width: 973
height: 466
m_MinSize: {x: 202, y: 221}
m_MaxSize: {x: 4002, y: 4021}
m_ActualView: {fileID: 16}
m_Panes:
- {fileID: 16}
m_Selected: 0
m_LastSelected: 0
--- !u!114 &13
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12019, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_AutoRepaintOnSceneChange: 0
m_MinSize: {x: 275, y: 50}
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Inspector
m_Image: {fileID: -6905738622615590433, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_DepthBufferBits: 0
m_Pos:
serializedVersion: 2
x: 2
y: 19
width: 401
height: 887
m_ScrollPosition: {x: 0, y: 0}
m_InspectorMode: 0
m_PreviewResizer:
m_CachedPref: -160
m_ControlHash: -371814159
m_PrefName: Preview_InspectorPreview
m_PreviewWindow: {fileID: 0}
--- !u!114 &14
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12014, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_AutoRepaintOnSceneChange: 0
m_MinSize: {x: 230, y: 250}
m_MaxSize: {x: 10000, y: 10000}
m_TitleContent:
m_Text: Project
m_Image: {fileID: -7501376956915960154, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_DepthBufferBits: 0
m_Pos:
serializedVersion: 2
x: 2
y: 19
width: 286
height: 421
m_SearchFilter:
m_NameFilter:
m_ClassNames: []
m_AssetLabels: []
m_AssetBundleNames: []
m_VersionControlStates: []
m_ReferencingInstanceIDs:
m_ScenePaths: []
m_ShowAllHits: 0
m_SearchArea: 0
m_Folders:
- Assets
m_ViewMode: 0
m_StartGridSize: 64
m_LastFolders:
- Assets
m_LastFoldersGridSize: -1
m_LastProjectPath: /Users/danielbrauer/Unity Projects/New Unity Project 47
m_IsLocked: 0
m_FolderTreeState:
scrollPos: {x: 0, y: 0}
m_SelectedIDs: ee240000
m_LastClickedID: 9454
m_ExpandedIDs: ee24000000ca9a3bffffff7f
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
m_OriginalName:
m_EditFieldRect:
serializedVersion: 2
x: 0
y: 0
width: 0
height: 0
m_UserData: 0
m_IsWaitingForDelay: 0
m_IsRenaming: 0
m_OriginalEventType: 11
m_IsRenamingFilename: 1
m_ClientGUIView: {fileID: 0}
m_SearchString:
m_CreateAssetUtility:
m_EndAction: {fileID: 0}
m_InstanceID: 0
m_Path:
m_Icon: {fileID: 0}
m_ResourceFile:
m_AssetTreeState:
scrollPos: {x: 0, y: 0}
m_SelectedIDs: 68fbffff
m_LastClickedID: 0
m_ExpandedIDs: ee240000
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
m_OriginalName:
m_EditFieldRect:
serializedVersion: 2
x: 0
y: 0
width: 0
height: 0
m_UserData: 0
m_IsWaitingForDelay: 0
m_IsRenaming: 0
m_OriginalEventType: 11
m_IsRenamingFilename: 1
m_ClientGUIView: {fileID: 0}
m_SearchString:
m_CreateAssetUtility:
m_EndAction: {fileID: 0}
m_InstanceID: 0
m_Path:
m_Icon: {fileID: 0}
m_ResourceFile:
m_ListAreaState:
m_SelectedInstanceIDs: 68fbffff
m_LastClickedInstanceID: -1176
m_HadKeyboardFocusLastEvent: 0
m_ExpandedInstanceIDs: c6230000
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
m_OriginalName:
m_EditFieldRect:
serializedVersion: 2
x: 0
y: 0
width: 0
height: 0
m_UserData: 0
m_IsWaitingForDelay: 0
m_IsRenaming: 0
m_OriginalEventType: 11
m_IsRenamingFilename: 1
m_ClientGUIView: {fileID: 0}
m_CreateAssetUtility:
m_EndAction: {fileID: 0}
m_InstanceID: 0
m_Path:
m_Icon: {fileID: 0}
m_ResourceFile:
m_NewAssetIndexInList: -1
m_ScrollPosition: {x: 0, y: 0}
m_GridSize: 64
m_DirectoriesAreaWidth: 110
--- !u!114 &15
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12015, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_AutoRepaintOnSceneChange: 1
m_MinSize: {x: 200, y: 200}
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Game
m_Image: {fileID: -2087823869225018852, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_DepthBufferBits: 32
m_Pos:
serializedVersion: 2
x: 0
y: 19
width: 971
height: 421
m_MaximizeOnPlay: 0
m_Gizmos: 0
m_Stats: 0
m_SelectedSizes: 00000000000000000000000000000000000000000000000000000000000000000000000000000000
m_TargetDisplay: 0
m_ZoomArea:
m_HRangeLocked: 0
m_VRangeLocked: 0
m_HBaseRangeMin: -242.75
m_HBaseRangeMax: 242.75
m_VBaseRangeMin: -101
m_VBaseRangeMax: 101
m_HAllowExceedBaseRangeMin: 1
m_HAllowExceedBaseRangeMax: 1
m_VAllowExceedBaseRangeMin: 1
m_VAllowExceedBaseRangeMax: 1
m_ScaleWithWindow: 0
m_HSlider: 0
m_VSlider: 0
m_IgnoreScrollWheelUntilClicked: 0
m_EnableMouseInput: 1
m_EnableSliderZoom: 0
m_UniformScale: 1
m_UpDirection: 1
m_DrawArea:
serializedVersion: 2
x: 0
y: 17
width: 971
height: 404
m_Scale: {x: 2, y: 2}
m_Translation: {x: 485.5, y: 202}
m_MarginLeft: 0
m_MarginRight: 0
m_MarginTop: 0
m_MarginBottom: 0
m_LastShownAreaInsideMargins:
serializedVersion: 2
x: -242.75
y: -101
width: 485.5
height: 202
m_MinimalGUI: 1
m_defaultScale: 2
m_TargetTexture: {fileID: 0}
m_CurrentColorSpace: 0
m_LastWindowPixelSize: {x: 1942, y: 842}
m_ClearInEditMode: 1
m_NoCameraWarning: 1
m_LowResolutionForAspectRatios: 01000000000100000100
--- !u!114 &16
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12013, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_AutoRepaintOnSceneChange: 1
m_MinSize: {x: 200, y: 200}
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Scene
m_Image: {fileID: 2318424515335265636, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_DepthBufferBits: 32
m_Pos:
serializedVersion: 2
x: 0
y: 19
width: 971
height: 445
m_SceneLighting: 1
lastFramingTime: 0
m_2DMode: 0
m_isRotationLocked: 0
m_AudioPlay: 0
m_Position:
m_Target: {x: 0, y: 0, z: 0}
speed: 2
m_Value: {x: 0, y: 0, z: 0}
m_RenderMode: 0
m_ValidateTrueMetals: 0
m_SceneViewState:
showFog: 1
showMaterialUpdate: 0
showSkybox: 1
showFlares: 1
showImageEffects: 1
grid:
xGrid:
m_Target: 0
speed: 2
m_Value: 0
yGrid:
m_Target: 1
speed: 2
m_Value: 1
zGrid:
m_Target: 0
speed: 2
m_Value: 0
m_Rotation:
m_Target: {x: -0.08717229, y: 0.89959055, z: -0.21045254, w: -0.3726226}
speed: 2
m_Value: {x: -0.08717229, y: 0.89959055, z: -0.21045254, w: -0.3726226}
m_Size:
m_Target: 10
speed: 2
m_Value: 10
m_Ortho:
m_Target: 0
speed: 2
m_Value: 0
m_LastSceneViewRotation: {x: 0, y: 0, z: 0, w: 0}
m_LastSceneViewOrtho: 0
m_ReplacementShader: {fileID: 0}
m_ReplacementString:
m_LastLockedObject: {fileID: 0}
m_ViewIsLockedToObject: 0
--- !u!114 &17
MonoBehaviour:
m_ObjectHideFlags: 52
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 1
m_Script: {fileID: 12061, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_AutoRepaintOnSceneChange: 0
m_MinSize: {x: 200, y: 200}
m_MaxSize: {x: 4000, y: 4000}
m_TitleContent:
m_Text: Hierarchy
m_Image: {fileID: -590624980919486359, guid: 0000000000000000d000000000000000,
type: 0}
m_Tooltip:
m_DepthBufferBits: 0
m_Pos:
serializedVersion: 2
x: 2
y: 19
width: 286
height: 445
m_TreeViewState:
scrollPos: {x: 0, y: 0}
m_SelectedIDs: 68fbffff
m_LastClickedID: -1176
m_ExpandedIDs: 7efbffff00000000
m_RenameOverlay:
m_UserAcceptedRename: 0
m_Name:
m_OriginalName:
m_EditFieldRect:
serializedVersion: 2
x: 0
y: 0
width: 0
height: 0
m_UserData: 0
m_IsWaitingForDelay: 0
m_IsRenaming: 0
m_OriginalEventType: 11
m_IsRenamingFilename: 0
m_ClientGUIView: {fileID: 0}
m_SearchString:
m_ExpandedScenes:
-
m_CurrenRootInstanceID: 0
m_Locked: 0
m_CurrentSortingName: TransformSorting

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: eabc9546105bf4accac1fd62a63e88e6
timeCreated: 1487337779
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 5a9bcd70e6a4b4b05badaa72e827d8e0
folderAsset: yes
timeCreated: 1475835190
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 3ad9b87dffba344c89909c6d1b1c17e1
folderAsset: yes
timeCreated: 1475593892
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,242 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;
using System.IO;
using System.Reflection;
[CustomEditor(typeof(Readme))]
[InitializeOnLoad]
public class ReadmeEditor : Editor
{
static string s_ShowedReadmeSessionStateName = "ReadmeEditor.showedReadme";
static string s_ReadmeSourceDirectory = "Assets/TutorialInfo";
const float k_Space = 16f;
static ReadmeEditor()
{
EditorApplication.delayCall += SelectReadmeAutomatically;
}
static void RemoveTutorial()
{
if (EditorUtility.DisplayDialog("Remove Readme Assets",
$"All contents under {s_ReadmeSourceDirectory} will be removed, are you sure you want to proceed?",
"Proceed",
"Cancel"))
{
if (Directory.Exists(s_ReadmeSourceDirectory))
{
FileUtil.DeleteFileOrDirectory(s_ReadmeSourceDirectory);
FileUtil.DeleteFileOrDirectory(s_ReadmeSourceDirectory + ".meta");
}
else
{
Debug.Log($"Could not find the Readme folder at {s_ReadmeSourceDirectory}");
}
var readmeAsset = SelectReadme();
if (readmeAsset != null)
{
var path = AssetDatabase.GetAssetPath(readmeAsset);
FileUtil.DeleteFileOrDirectory(path + ".meta");
FileUtil.DeleteFileOrDirectory(path);
}
AssetDatabase.Refresh();
}
}
static void SelectReadmeAutomatically()
{
if (!SessionState.GetBool(s_ShowedReadmeSessionStateName, false))
{
var readme = SelectReadme();
SessionState.SetBool(s_ShowedReadmeSessionStateName, true);
if (readme && !readme.loadedLayout)
{
LoadLayout();
readme.loadedLayout = true;
}
}
}
static void LoadLayout()
{
var assembly = typeof(EditorApplication).Assembly;
var windowLayoutType = assembly.GetType("UnityEditor.WindowLayout", true);
var method = windowLayoutType.GetMethod("LoadWindowLayout", BindingFlags.Public | BindingFlags.Static);
method.Invoke(null, new object[] { Path.Combine(Application.dataPath, "TutorialInfo/Layout.wlt"), false });
}
static Readme SelectReadme()
{
var ids = AssetDatabase.FindAssets("Readme t:Readme");
if (ids.Length == 1)
{
var readmeObject = AssetDatabase.LoadMainAssetAtPath(AssetDatabase.GUIDToAssetPath(ids[0]));
Selection.objects = new UnityEngine.Object[] { readmeObject };
return (Readme)readmeObject;
}
else
{
Debug.Log("Couldn't find a readme");
return null;
}
}
protected override void OnHeaderGUI()
{
var readme = (Readme)target;
Init();
var iconWidth = Mathf.Min(EditorGUIUtility.currentViewWidth / 3f - 20f, 128f);
GUILayout.BeginHorizontal("In BigTitle");
{
if (readme.icon != null)
{
GUILayout.Space(k_Space);
GUILayout.Label(readme.icon, GUILayout.Width(iconWidth), GUILayout.Height(iconWidth));
}
GUILayout.Space(k_Space);
GUILayout.BeginVertical();
{
GUILayout.FlexibleSpace();
GUILayout.Label(readme.title, TitleStyle);
GUILayout.FlexibleSpace();
}
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
}
GUILayout.EndHorizontal();
}
public override void OnInspectorGUI()
{
var readme = (Readme)target;
Init();
foreach (var section in readme.sections)
{
if (!string.IsNullOrEmpty(section.heading))
{
GUILayout.Label(section.heading, HeadingStyle);
}
if (!string.IsNullOrEmpty(section.text))
{
GUILayout.Label(section.text, BodyStyle);
}
if (!string.IsNullOrEmpty(section.linkText))
{
if (LinkLabel(new GUIContent(section.linkText)))
{
Application.OpenURL(section.url);
}
}
GUILayout.Space(k_Space);
}
if (GUILayout.Button("Remove Readme Assets", ButtonStyle))
{
RemoveTutorial();
}
}
bool m_Initialized;
GUIStyle LinkStyle
{
get { return m_LinkStyle; }
}
[SerializeField]
GUIStyle m_LinkStyle;
GUIStyle TitleStyle
{
get { return m_TitleStyle; }
}
[SerializeField]
GUIStyle m_TitleStyle;
GUIStyle HeadingStyle
{
get { return m_HeadingStyle; }
}
[SerializeField]
GUIStyle m_HeadingStyle;
GUIStyle BodyStyle
{
get { return m_BodyStyle; }
}
[SerializeField]
GUIStyle m_BodyStyle;
GUIStyle ButtonStyle
{
get { return m_ButtonStyle; }
}
[SerializeField]
GUIStyle m_ButtonStyle;
void Init()
{
if (m_Initialized)
return;
m_BodyStyle = new GUIStyle(EditorStyles.label);
m_BodyStyle.wordWrap = true;
m_BodyStyle.fontSize = 14;
m_BodyStyle.richText = true;
m_TitleStyle = new GUIStyle(m_BodyStyle);
m_TitleStyle.fontSize = 26;
m_HeadingStyle = new GUIStyle(m_BodyStyle);
m_HeadingStyle.fontStyle = FontStyle.Bold;
m_HeadingStyle.fontSize = 18;
m_LinkStyle = new GUIStyle(m_BodyStyle);
m_LinkStyle.wordWrap = false;
// Match selection color which works nicely for both light and dark skins
m_LinkStyle.normal.textColor = new Color(0x00 / 255f, 0x78 / 255f, 0xDA / 255f, 1f);
m_LinkStyle.stretchWidth = false;
m_ButtonStyle = new GUIStyle(EditorStyles.miniButton);
m_ButtonStyle.fontStyle = FontStyle.Bold;
m_Initialized = true;
}
bool LinkLabel(GUIContent label, params GUILayoutOption[] options)
{
var position = GUILayoutUtility.GetRect(label, LinkStyle, options);
Handles.BeginGUI();
Handles.color = LinkStyle.normal.textColor;
Handles.DrawLine(new Vector3(position.xMin, position.yMax), new Vector3(position.xMax, position.yMax));
Handles.color = Color.white;
Handles.EndGUI();
EditorGUIUtility.AddCursorRect(position, MouseCursor.Link);
return GUI.Button(position, label, LinkStyle);
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 476cc7d7cd9874016adc216baab94a0a
timeCreated: 1484146680
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,16 @@
using System;
using UnityEngine;
public class Readme : ScriptableObject
{
public Texture2D icon;
public string title;
public Section[] sections;
public bool loadedLayout;
[Serializable]
public class Section
{
public string heading, text, linkText, url;
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: fcf7219bab7fe46a1ad266029b2fee19
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- icon: {instanceID: 0}
executionOrder: 0
icon: {fileID: 2800000, guid: a186f8a87ca4f4d3aa864638ad5dfb65, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 18dc0cd2c080841dea60987a38ce93fa
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,324 @@
# Changelog
All notable changes to this package will be documented in this file.
## [12.0.1] - 2025-01-16
### Preview Generator changes
- Updated generated preview collection UI to display the asset extension
- Fixed an issue with some prefab and model asset types not generating previews
- Fixed an error that could occur when changing scenes after deleting a preview source asset
## [12.0.0] - 2025-01-13
### General changes
- The code comprising the Asset Store Publishing Tools has been refactored.
- Added dependency on Newtonsoft Json
### Uploader changes
- Updated window to retain its state if closed unless a domain reload occurs
- Added option to generate higher resolution asset previews when exporting
- Fixed a rare issue where authentication would fail
- Minor UI tweaks
### Validator changes
- Added validation tests for:
- Package naming
- Project Template assets
- Updated the Type Namespace validation test to check for Unity top level namespaces
### Exporter changes
- Updated how asset previews are generated/included for the package that is being exported
### Preview Generator
- Added a Preview Generator window that can be used to pre-generate and inspect package previews before exporting
## [11.4.4] - 2024-11-29
### Validator Changes
- The validator UI window description section can now be expanded or shrunk to take up less screen space
- Updated severity of the Model Importer Logs validation test
### Exporter Changes
- Updated exporter to always exclude hidden files and folders beginning with the dot symbol (e.g.: .hiddenFolder/ or .hiddenfile.txt)
- Updated exporter to explicitly exclude extended attribute files when exporting packages on macOS systems
### Other
- Moved the Asset Store Tools toolbar items into the Tools section
- Fixed several window-related compilation warnings when using Unity 6 versions of the Editor
## [11.4.3] - 2024-08-01
### Validator Changes
- Hotfix: Remove non-ascii characters from the demo scene validation
## [11.4.2] - 2024-07-30
### Validator Changes
- Check for nested .unitypackage files in the demo scene validation
- Prevent normal map test from erroring when misc importer types are detected
- Remove Templates category from the uncompressed images requirement list
## [11.4.1] - 2024-05-10
### Exporter Changes
- Fixed an issue with bundled plugin folder contents not being exported
### Other
- Miscellaneous internal changes
## [11.4.0] - 2024-01-23
### Uploader Changes
- Added prevention of uploading packages larger than 6 GB
- Added a prompt to allow automatically generating meta files within hidden folders
- Fixed some obsolete API usage warnings in newer Unity versions
### Validator Changes
- Added validation tests for:
- Animation Clip take names
- Model import logs
- Uncompressed Package size
- Updated the fail severity of Audio Clipping validation test
- Updated the Demo Scene test to treat default scenes with custom skyboxes as valid demo scenes
- Fixed some obsolete API usage warnings in newer Unity versions
### Other
- Added an option to check for Asset Store Publishing Tools updates
## [11.3.1] - 2023-08-14
### Uploader Changes
- Added the option to select indirect package dependencies from the project (e.g. Mathematics package installed by the Burst package)
### Validator Changes
- Updated the Texture Dimensions test to ignore 'Sprite' and 'Editor GUI' texture types
### Exporter Changes
- Updated exporter to ignore the 'ProjectSettings/ProjectVersion.txt' asset when exporting 'Complete Project' category packages
## [11.3.0] - 2023-07-04
### Uploader Changes
- Added the option to validate a pre-exported package
- Added the option to export a .unitypackage file without uploading
- Updated the dependency selection UI
### Validator Changes
- Added the option to validate several asset paths at once
- Note: when validating package that is comprised of several folders (e.g. Assets/MyPackage +
Assets/StreamingAssets + Assets/WebGLTemplates), please select all applicable paths that would be included in the package
- Added several new validation tests for:
- File Menu Names
- Compressed files
- Model Types
- Texture Dimensions
- Particle Systems
- Normal Map Textures
- Audio Clipping
- Path Lengths
- Script Compilation
- Updated validation test severities based on package category
- Updated validation tests to each have their own test logic class
- Updated validation tests to be displayed in alphabetical order
- Fixed several issues with the namespace check test
- Fixed scenes in Samples~ folders not being taken into account for the sample scene check test
- Other internal changes
### Exporter Changes
- Package exporter is now a separate module (similar to Uploader and Validator)
- Fixed hidden folders being included when exporting package content
- Note: this prevents an issue with the Unity Editor, where exported hidden folders would appear in the Project window
as empty folders when imported, despite having content on disk. Content nested within hidden folders is still collected,
provided it contains unique .meta files
## [11.2.2] - 2023-02-23
### Validator Changes
- Updated the 'LOD Setup' test to address some issues
- Added additional checks for LOD renderers (inactive renderer check, LOD Group reference check, relative hierarchy position to LOD Group check)
- LOD Group Component is no longer required to be on the root of the Prefab
- Updated the test result message interface when invalid Prefabs are found
## [11.2.1] - 2023-01-17
### Uploader Changes
- Added a more informative error when exporting content with clashing guid meta files in hidden folders
- Fixed a compilation issue for Unity 2020.1 and 2020.2
- Fixed a rare error condition when queueing multiple package uploads in quick succession
- Fixed Asset Store Uploader state not being properly reset if the uploading process fails
### Validator Changes
- Updated the Asset Store Validator description
- Fixed a rare memory overflow issue when performing package validation
## [11.2.0] - 2022-11-03
### Uploader Changes
- Uploader will now use the custom package exporter by default
- An option to use the legacy (native) exporter can be found in the Asset Store Publishing Tools' settings window
- When exporting from the Assets folder, package dependencies can now be selected individually instead of being a choice between 'All' or 'None'
- This option is only available with the custom exporter
- Changed the way the Uploader reports completed uploading tasks
- Modal pop-up has been replaced by a new UI view state
- Added an option to the Asset Store Publishing Tools' Settings to display the pop-up after a completed upload
- Changed exported .unitypackage files to have distinguishable file names
- Fixed the Uploader window indefinitely stalling at 100% upload progress when a response from the Asset Store server is not received
- Fixed native package exporter producing broken packages when the export path contained hidden folders
- Fixed an issue with high CPU usage when uploading packages
- Fixed Asset Store Publishing Tools' settings not being saved between Editor sessions on macOS
- Other minor changes and tweaks
### Validator Changes
- Added two new tests:
- 'Types have namespaces': checks whether scripts and native libraries under the validated path are nested under a namespace
- 'Consistent line endings': checks whether scripts under the validated path have consistent line endings. This is similar to the warning from the Unity Editor compilation pipeline when a script contains both Windows and UNIX line endings.
- Improved 'Reset Prefabs' test to display and be more informative about prefabs with unusually low transform values
- Improved 'SpeedTree asset inclusion' test to search for '.st' files
- Improved 'Documentation inclusion' test to treat '.md' files as valid documentation files
- Improved 'Lossy audio file inclusion' test to treat '.aif' and '.aiff' files as valid non-lossy audio files
- Improved 'Lossy audio file inclusion' test to search the project for non-lossy variants of existing lossy audio files
- Removed 'Duplicate animation names' test
- Tweaked validation severities for several tests
- Other minor changes and tweaks
## [11.1.0] - 2022-09-14
### Uploader Changes
- Package Publisher Portal links can now be opened for all packages regardless of package status
- External Dependency Manager can now be selected as a 'Special Folder' if found in the root Assets folder
### Validator Changes
- Added category selection for the Validator
- Categories help determine the outcome of package validation more accurately. For example, documentation is not crucial for art packages, but is required for tooling packages.
- Added a list of prefabs with missing mesh references to 'Meshes have Prefabs' test when the test fails
- Corrected the message for a passing 'Shader compilation errors' test
- Improved the floating point precision accuracy of 'Reset Prefabs' test
- Fixed 'Missing Components in Assets' test checking all project folders instead of only the set path
- Fixed 'Prefabs for meshes' test not checking meshes in certain paths
- Fixed 'Reset Prefabs' test failing because of Prefabs with a Rect Transform Component
- Fixed 'Reset Prefabs' test ignoring Transform rotation
- Fixed test description text overlapping in some cases
- Other minor changes and tweaks
## [11.0.2] - 2022-08-09
- Corrected some namespaces which were causing issues when deriving classes from Editor class
## [11.0.1] - 2022-08-05
### Uploader Changes
- Added Settings window (Asset Store Tools > Settings)
- Added Soft/Junction Symlink support (enable through Settings)
- Added workflow and path selection serialization (workflow saved locally, paths locally and online)
- No more logs when using the `-nullable` compiler option (thanks @alfish)
- Some API refactoring in preparation for CLI support
- Other minor fixes/improvements
**Note:** when updating Asset Store Tools from the Package Manager, don't forget to remove the old version from the project (V11.0.0) before importing the new one (V11.0.1)
## [11.0.0] - 2022-07-20
### Uploader changes
- UI has been reworked using UI Toolkit
- New login window, allowing to login using Unity Cloud Services
- Improved top bar, including search and sorting
- Draft packages moved to the top
- Added category, size, and last modified date next to the package
- Added a link to the publishing portal next to the package
- New uploading flow: “Pre-exported .unitypackage”
- Previous uploading flow (folder selection) has been renamed to “From Assets Folder”
- Dependencies check has been renamed to “Include Package Manifest” for clarity
- Special Folders can now be selected and uploaded together with the packages main folder (i.e. StreamingAssets, Plugins)
- You can now upload to multiple packages at the same time without waiting for the first one to finish
- Package can now be validated in the Uploading window by pressing the “Validate” button
- Added refresh and logout buttons to the bottom toolbar for easier access
- Packages caching - package information will no longer be redownloaded every time you open the Uploader window during the same Editor session
- (Experimental) Custom exporter - will export your package ~2 times faster, but may miss some asset previews in the final product. To enable it - click three dots on the top left side of the window and enable “Use Custom Exporting”
### Validator changes
- UI has been reworked using UI Toolkit
- New tests based on the new guidelines
- Updated tests titles, descriptions, and error reporting
## [5.0.5] - 2021-11-04
- Fixed namespace issues
## [5.0.4] - 2020-07-28
- Fixed issues with Unity 2020.1
## [5.0.3] - 2020-05-07
- Remove "Remove Standard Assets" check
## [5.0.2] - 2020-04-21
- Enable auto login with Unity account
- Upload package with thread
## [5.0.1] - 2020-03-23
- Fix domain resolve issue
## [5.0.0] - 2019-10-09
- Added "Package Validator" tool
- Added Help window
- Added logout confirmation popup
- Updated toolbar menu layout
- Removed "Mass Labeler" tool
- Updated layout of Login and Package Upload windows
- Error messages are now more elaborate and user-friendly
- Removed deprecated "Main Assets" step from the Package Upload window
- Package Upload window now has a step for including package manager dependencies
- Tooltips are now added to each upload process step
## [4.1.0] - 2018-05-14
- Made Tool compatible with 2017.1
## [4.0.7] - 2017-07-10
- Tweaked menu items.
## [4.0.6] - 2016-07-15
- Improved error messages.
## [4.0.5] - 2016-03-17
- Enabling upload of fbm files.
## [4.0.4] - 2015-11-16
- Login improvements
## [4.0.3] - 2015-11-16
- Prepare the Tools for Unity 5.3
## [4.0.2] - 2015-10-23
- Fixed issue where Upload button would not work for some projects.
- Fixed issues for publishers that only had one package.
## [4.0.0] - 2015-09-01
- Replaced Package Manager with Package Upload. Package management is now handled by Publisher Administration

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 06607220dbd46414e8f66bf9c5e3eb79
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 166da5c6fc70e814a8262463903b2714
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d48a2325d352e7a4cae56d3f8eeaab2d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 25799fb31cd475347af7f5442c231797
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,48 @@
using AssetStoreTools.Api.Responses;
using AssetStoreTools.Utility;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
namespace AssetStoreTools.Api
{
internal abstract class AuthenticationBase : IAuthenticationType
{
protected Uri LoginUrl = ApiUtility.CreateUri(Constants.Api.AuthenticateUrl, true);
protected FormUrlEncodedContent AuthenticationContent;
protected FormUrlEncodedContent GetAuthenticationContent(params KeyValuePair<string, string>[] content)
{
var baseContent = Constants.Api.DefaultAssetStoreQuery();
try { baseContent.Add("license_hash", ApiUtility.GetLicenseHash()); } catch { ASDebug.LogWarning("Could not retrieve license hash"); }
try { baseContent.Add("hardware_hash", ApiUtility.GetHardwareHash()); } catch { ASDebug.LogWarning("Could not retrieve hardware hash"); }
foreach (var extraContent in content)
{
baseContent.Add(extraContent.Key, extraContent.Value);
}
return new FormUrlEncodedContent(baseContent);
}
protected AuthenticationResponse ParseResponse(HttpResponseMessage response)
{
try
{
response.EnsureSuccessStatusCode();
var responseString = response.Content.ReadAsStringAsync().Result;
return new AuthenticationResponse(responseString);
}
catch (HttpRequestException e)
{
return new AuthenticationResponse(response.StatusCode, e) { Success = false };
}
}
public abstract Task<AuthenticationResponse> Authenticate(IAssetStoreClient client, CancellationToken cancellationToken = default);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f677e03f1be1048439a1fa5e7a0a37b6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,21 @@
using AssetStoreTools.Api.Models;
using AssetStoreTools.Api.Responses;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AssetStoreTools.Api
{
internal interface IAssetStoreApi
{
Task<AssetStoreToolsVersionResponse> GetLatestAssetStoreToolsVersion(CancellationToken cancellationToken = default);
Task<AuthenticationResponse> Authenticate(IAuthenticationType authenticationType, CancellationToken cancellationToken = default);
void Deauthenticate();
Task<PackagesDataResponse> GetPackages(CancellationToken cancellationToken = default);
Task<CategoryDataResponse> GetCategories(CancellationToken cancellationToken = default);
Task<PackageThumbnailResponse> GetPackageThumbnail(Package package, CancellationToken cancellationToken = default);
Task<RefreshedPackageDataResponse> RefreshPackageMetadata(Package package, CancellationToken cancellationToken = default);
Task<PackageUploadedUnityVersionDataResponse> GetPackageUploadedVersions(Package package, CancellationToken cancellationToken = default);
Task<PackageUploadResponse> UploadPackage(IPackageUploader uploader, IProgress<float> progress = null, CancellationToken cancellationToken = default);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e616488c25d278741bb0d08168219309
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace AssetStoreTools.Api
{
internal interface IAssetStoreClient
{
void SetSessionId(string sessionId);
void ClearSessionId();
Task<HttpResponseMessage> Get(Uri uri, CancellationToken cancellationToken = default);
Task<HttpResponseMessage> Post(Uri uri, HttpContent content, CancellationToken cancellationToken = default);
Task<HttpResponseMessage> Put(Uri uri, HttpContent content, CancellationToken cancellationToken = default);
Task<HttpResponseMessage> Send(HttpRequestMessage request, CancellationToken cancellationToken = default);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b2bbadec62178cc4189e605367b219e7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,11 @@
using AssetStoreTools.Api.Responses;
using System.Threading;
using System.Threading.Tasks;
namespace AssetStoreTools.Api
{
internal interface IAuthenticationType
{
Task<AuthenticationResponse> Authenticate(IAssetStoreClient client, CancellationToken cancellationToken);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0000dcd6975bc8e4abc546a19f194040
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,12 @@
using AssetStoreTools.Api.Responses;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AssetStoreTools.Api
{
internal interface IPackageUploader
{
Task<PackageUploadResponse> Upload(IAssetStoreClient client, IProgress<float> progress, CancellationToken cancellationToken = default);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0fc6c47b1c0a65540a40efbf1491193b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,59 @@
using AssetStoreTools.Api.Responses;
using System;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace AssetStoreTools.Api
{
internal abstract class PackageUploaderBase : IPackageUploader
{
protected const int UploadChunkSizeBytes = 32768;
protected const int UploadResponseTimeoutMs = 10000;
protected abstract void ValidateSettings();
public abstract Task<PackageUploadResponse> Upload(IAssetStoreClient client, IProgress<float> progress = null, CancellationToken cancellationToken = default);
protected void EnsureSuccessResponse(HttpResponseMessage response)
{
try
{
response.EnsureSuccessStatusCode();
}
catch
{
throw new Exception(response.Content.ReadAsStringAsync().Result);
}
}
protected void WaitForUploadCompletion(Task<HttpResponseMessage> response, FileStream requestFileStream, IProgress<float> progress, CancellationToken cancellationToken)
{
// Progress tracking
int updateIntervalMs = 100;
bool allBytesSent = false;
DateTime timeOfCompletion = default;
while (!response.IsCompleted)
{
float uploadProgress = (float)requestFileStream.Position / requestFileStream.Length * 100;
progress?.Report(uploadProgress);
Thread.Sleep(updateIntervalMs);
// A timeout for rare cases, when package uploading reaches 100%, but Put task IsComplete value remains 'False'
if (requestFileStream.Position == requestFileStream.Length)
{
if (!allBytesSent)
{
allBytesSent = true;
timeOfCompletion = DateTime.UtcNow;
}
else if (DateTime.UtcNow.Subtract(timeOfCompletion).TotalMilliseconds > UploadResponseTimeoutMs)
{
throw new TimeoutException();
}
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2718ddd16e425ba4a82ab973724bcff7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,76 @@
using AssetStoreTools.Api.Models;
using AssetStoreTools.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditorInternal;
namespace AssetStoreTools.Api
{
internal class ApiUtility
{
public static Uri CreateUri(string url, bool includeDefaultAssetStoreQuery) => CreateUri(url, null, includeDefaultAssetStoreQuery);
public static Uri CreateUri(string url, IDictionary<string, string> queryParameters, bool includeDefaultAssetStoreQuery)
{
IDictionary<string, string> fullQueryParameters = includeDefaultAssetStoreQuery ?
Constants.Api.DefaultAssetStoreQuery() : new Dictionary<string, string>();
if (queryParameters != null && queryParameters.Count > 0)
{
foreach (var kvp in queryParameters)
fullQueryParameters.Add(kvp);
}
var builder = new UriBuilder(url);
if (fullQueryParameters.Count == 0)
return builder.Uri;
var fullQueryParameterString = string.Empty;
foreach (var queryParam in fullQueryParameters)
{
var escapedValue = queryParam.Value != null ? Uri.EscapeDataString(queryParam.Value) : string.Empty;
fullQueryParameterString += $"{queryParam.Key}={escapedValue}&";
}
fullQueryParameterString = fullQueryParameterString.Remove(fullQueryParameterString.Length - 1);
builder.Query = fullQueryParameterString;
return builder.Uri;
}
public static List<Package> CombinePackageData(List<Package> mainPackageData, List<PackageAdditionalData> extraPackageData, List<Category> categoryData)
{
foreach (var package in mainPackageData)
{
var extraData = extraPackageData.FirstOrDefault(x => package.PackageId == x.PackageId);
if (extraData == null)
{
ASDebug.LogWarning($"Could not find extra data for Package {package.PackageId}");
continue;
}
var categoryId = extraData.CategoryId;
var category = categoryData.FirstOrDefault(x => x.Id.ToString() == categoryId);
if (category != null)
package.Category = category.Name;
else
package.Category = "Unknown";
package.Modified = extraData.Modified;
package.Size = extraData.Size;
}
return mainPackageData;
}
public static string GetLicenseHash()
{
return InternalEditorUtility.GetAuthToken().Substring(0, 40);
}
public static string GetHardwareHash()
{
return InternalEditorUtility.GetAuthToken().Substring(40, 40);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5becec0b3c0ba274fb0b01544e63b6c4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,268 @@
using AssetStoreTools.Api.Models;
using AssetStoreTools.Api.Responses;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace AssetStoreTools.Api
{
internal class AssetStoreApi : IAssetStoreApi
{
private IAssetStoreClient _client;
public AssetStoreApi(IAssetStoreClient client)
{
_client = client;
}
public async Task<AssetStoreToolsVersionResponse> GetLatestAssetStoreToolsVersion(CancellationToken cancellationToken = default)
{
try
{
var uri = ApiUtility.CreateUri(Constants.Api.AssetStoreToolsLatestVersionUrl, false);
var response = await _client.Get(uri, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
response.EnsureSuccessStatusCode();
var responseStr = response.Content.ReadAsStringAsync().Result;
return new AssetStoreToolsVersionResponse(responseStr);
}
catch (OperationCanceledException e)
{
return new AssetStoreToolsVersionResponse() { Success = false, Cancelled = true, Exception = e };
}
catch (Exception e)
{
return new AssetStoreToolsVersionResponse() { Success = false, Exception = e };
}
}
public async Task<AuthenticationResponse> Authenticate(IAuthenticationType authenticationType, CancellationToken cancellationToken = default)
{
try
{
var loginResponse = await authenticationType.Authenticate(_client, cancellationToken);
if (loginResponse.Success)
{
_client.SetSessionId(loginResponse.User.SessionId);
}
return loginResponse;
}
catch (OperationCanceledException e)
{
return new AuthenticationResponse() { Success = false, Cancelled = true, Exception = e };
}
catch (Exception e)
{
return new AuthenticationResponse() { Success = false, Exception = e };
}
}
public void Deauthenticate()
{
_client.ClearSessionId();
}
public async Task<PackagesDataResponse> GetPackages(CancellationToken cancellationToken = default)
{
try
{
var mainDataResponse = await GetPackageDataMain(cancellationToken);
if (!mainDataResponse.Success)
throw mainDataResponse.Exception;
var additionalDataResponse = await GetPackageDataExtra(cancellationToken);
if (!additionalDataResponse.Success)
throw additionalDataResponse.Exception;
var categoryDataResponse = await GetCategories(cancellationToken);
if (!categoryDataResponse.Success)
throw categoryDataResponse.Exception;
var joinedData = ApiUtility.CombinePackageData(mainDataResponse.Packages, additionalDataResponse.Packages, categoryDataResponse.Categories);
return new PackagesDataResponse() { Success = true, Packages = joinedData };
}
catch (OperationCanceledException e)
{
return new PackagesDataResponse() { Success = false, Cancelled = true, Exception = e };
}
catch (Exception e)
{
return new PackagesDataResponse() { Success = false, Exception = e };
}
}
private async Task<PackagesDataResponse> GetPackageDataMain(CancellationToken cancellationToken)
{
try
{
var uri = ApiUtility.CreateUri(Constants.Api.GetPackagesUrl, true);
var response = await _client.Get(uri, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
response.EnsureSuccessStatusCode();
var responseStr = response.Content.ReadAsStringAsync().Result;
return new PackagesDataResponse(responseStr);
}
catch (OperationCanceledException e)
{
return new PackagesDataResponse() { Success = false, Cancelled = true, Exception = e };
}
catch (Exception e)
{
return new PackagesDataResponse() { Success = false, Exception = e };
}
}
private async Task<PackagesAdditionalDataResponse> GetPackageDataExtra(CancellationToken cancellationToken)
{
try
{
var uri = ApiUtility.CreateUri(Constants.Api.GetPackagesAdditionalDataUrl, true);
var response = await _client.Get(uri, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
response.EnsureSuccessStatusCode();
var responseStr = response.Content.ReadAsStringAsync().Result;
return new PackagesAdditionalDataResponse(responseStr);
}
catch (OperationCanceledException e)
{
return new PackagesAdditionalDataResponse() { Success = false, Cancelled = true, Exception = e };
}
catch (Exception e)
{
return new PackagesAdditionalDataResponse() { Success = false, Exception = e };
}
}
public async Task<CategoryDataResponse> GetCategories(CancellationToken cancellationToken)
{
try
{
var uri = ApiUtility.CreateUri(Constants.Api.GetCategoriesUrl, true);
var response = await _client.Get(uri, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
response.EnsureSuccessStatusCode();
var responseStr = response.Content.ReadAsStringAsync().Result;
return new CategoryDataResponse(responseStr);
}
catch (OperationCanceledException e)
{
return new CategoryDataResponse() { Success = false, Cancelled = true, Exception = e };
}
catch (Exception e)
{
return new CategoryDataResponse() { Success = false, Exception = e };
}
}
public async Task<PackageThumbnailResponse> GetPackageThumbnail(Package package, CancellationToken cancellationToken = default)
{
try
{
if (string.IsNullOrEmpty(package.IconUrl))
throw new Exception($"Could not retrieve thumbnail for package {package.PackageId} - icon url is null");
var response = await _client.Get(new Uri(package.IconUrl), cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
response.EnsureSuccessStatusCode();
var responseBytes = response.Content.ReadAsByteArrayAsync().Result;
return new PackageThumbnailResponse(responseBytes);
}
catch (OperationCanceledException e)
{
return new PackageThumbnailResponse() { Success = false, Cancelled = true, Exception = e };
}
catch (Exception e)
{
return new PackageThumbnailResponse() { Success = false, Exception = e };
}
}
public async Task<RefreshedPackageDataResponse> RefreshPackageMetadata(Package package, CancellationToken cancellationToken = default)
{
try
{
var refreshedPackage = JObject.FromObject(package).DeepClone().ToObject<Package>();
var packagesResponse = await GetPackageDataExtra(cancellationToken);
if (!packagesResponse.Success)
throw packagesResponse.Exception;
// Find the updated package data in the latest data json
var packageRefreshSource = packagesResponse.Packages.FirstOrDefault(x => x.PackageId == refreshedPackage.PackageId);
if (packageRefreshSource == null)
return new RefreshedPackageDataResponse() { Success = false, Exception = new MissingMemberException($"Unable to find downloaded package data for package id {package.PackageId}") };
// Retrieve the category map
var categoryData = await GetCategories(cancellationToken);
if (!categoryData.Success)
return new RefreshedPackageDataResponse() { Success = false, Exception = packagesResponse.Exception };
// Update the package data
refreshedPackage.Name = packageRefreshSource.Name;
refreshedPackage.Status = packageRefreshSource.Status;
var newCategory = categoryData.Categories.FirstOrDefault(x => x.Id.ToString() == packageRefreshSource.CategoryId);
refreshedPackage.Category = newCategory != null ? newCategory.Name : "Unknown";
refreshedPackage.Modified = packageRefreshSource.Modified;
refreshedPackage.Size = packageRefreshSource.Size;
return new RefreshedPackageDataResponse() { Success = true, Package = refreshedPackage };
}
catch (OperationCanceledException)
{
return new RefreshedPackageDataResponse() { Success = false, Cancelled = true };
}
catch (Exception e)
{
return new RefreshedPackageDataResponse() { Success = false, Exception = e };
}
}
public async Task<PackageUploadedUnityVersionDataResponse> GetPackageUploadedVersions(Package package, CancellationToken cancellationToken = default)
{
try
{
var uri = ApiUtility.CreateUri(Constants.Api.GetPackageUploadedVersionsUrl(package.PackageId, package.VersionId), true);
var response = await _client.Get(uri, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
response.EnsureSuccessStatusCode();
var responseStr = response.Content.ReadAsStringAsync().Result;
return new PackageUploadedUnityVersionDataResponse(responseStr);
}
catch (OperationCanceledException e)
{
return new PackageUploadedUnityVersionDataResponse() { Success = false, Cancelled = true, Exception = e };
}
catch (Exception e)
{
return new PackageUploadedUnityVersionDataResponse() { Success = false, Exception = e };
}
}
public async Task<PackageUploadResponse> UploadPackage(IPackageUploader uploader, IProgress<float> progress = null, CancellationToken cancellationToken = default)
{
try
{
return await uploader.Upload(_client, progress, cancellationToken);
}
catch (OperationCanceledException e)
{
return new PackageUploadResponse() { Success = false, Cancelled = true, Exception = e };
}
catch (Exception e)
{
return new PackageUploadResponse() { Success = false, Exception = e };
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 684fca3fffd79d944a32d9b3adbfc007
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,55 @@
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace AssetStoreTools.Api
{
internal class AssetStoreClient : IAssetStoreClient
{
private HttpClient _httpClient;
public AssetStoreClient()
{
ServicePointManager.DefaultConnectionLimit = 500;
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.ConnectionClose = false;
_httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
_httpClient.Timeout = TimeSpan.FromMinutes(1320);
}
public void SetSessionId(string sessionId)
{
ClearSessionId();
if (!string.IsNullOrEmpty(sessionId))
_httpClient.DefaultRequestHeaders.Add("X-Unity-Session", sessionId);
}
public void ClearSessionId()
{
_httpClient.DefaultRequestHeaders.Remove("X-Unity-Session");
}
public Task<HttpResponseMessage> Get(Uri uri, CancellationToken cancellationToken = default)
{
return _httpClient.GetAsync(uri, cancellationToken);
}
public Task<HttpResponseMessage> Post(Uri uri, HttpContent content, CancellationToken cancellationToken = default)
{
return _httpClient.PostAsync(uri, content, cancellationToken);
}
public Task<HttpResponseMessage> Put(Uri uri, HttpContent content, CancellationToken cancellationToken = default)
{
return _httpClient.PutAsync(uri, content, cancellationToken);
}
public Task<HttpResponseMessage> Send(HttpRequestMessage request, CancellationToken cancellationToken = default)
{
return _httpClient.SendAsync(request, cancellationToken);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 80b4527c908161a4b9f06dc393b502f9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,25 @@
using AssetStoreTools.Api.Responses;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AssetStoreTools.Api
{
internal class CloudTokenAuthentication : AuthenticationBase
{
public CloudTokenAuthentication(string cloudToken)
{
AuthenticationContent = GetAuthenticationContent(
new KeyValuePair<string, string>("user_access_token", cloudToken)
);
}
public override async Task<AuthenticationResponse> Authenticate(IAssetStoreClient client, CancellationToken cancellationToken)
{
var result = await client.Post(LoginUrl, AuthenticationContent, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
return ParseResponse(result);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 99f1baec74f26a34bb972b19c92d523f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,26 @@
using AssetStoreTools.Api.Responses;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace AssetStoreTools.Api
{
internal class CredentialsAuthentication : AuthenticationBase
{
public CredentialsAuthentication(string email, string password)
{
AuthenticationContent = GetAuthenticationContent(
new KeyValuePair<string, string>("user", email),
new KeyValuePair<string, string>("pass", password)
);
}
public override async Task<AuthenticationResponse> Authenticate(IAssetStoreClient client, CancellationToken cancellationToken)
{
var result = await client.Post(LoginUrl, AuthenticationContent, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
return ParseResponse(result);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 353e556b63fd441428f387bc85aa612c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1f83e4b5507886f4b873c22c146b8f6a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,58 @@
using Newtonsoft.Json.Serialization;
using System.Collections.Generic;
namespace AssetStoreTools.Api.Models
{
internal class Category
{
public int Id { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public class AssetStoreCategoryResolver : DefaultContractResolver
{
private Dictionary<string, string> _propertyConversions;
public AssetStoreCategoryResolver()
{
_propertyConversions = new Dictionary<string, string>()
{
{ nameof(Category.Name), "assetstore_name" }
};
}
protected override string ResolvePropertyName(string propertyName)
{
if (_propertyConversions.ContainsKey(propertyName))
return _propertyConversions[propertyName];
return base.ResolvePropertyName(propertyName);
}
}
public class CachedCategoryResolver : DefaultContractResolver
{
private static CachedCategoryResolver _instance;
public static CachedCategoryResolver Instance => _instance ?? (_instance = new CachedCategoryResolver());
private Dictionary<string, string> _propertyConversion;
private CachedCategoryResolver()
{
this.NamingStrategy = new SnakeCaseNamingStrategy();
_propertyConversion = new Dictionary<string, string>()
{
{ nameof(Category.Name), "name" }
};
}
protected override string ResolvePropertyName(string propertyName)
{
if (_propertyConversion.ContainsKey(propertyName))
return _propertyConversion[propertyName];
return base.ResolvePropertyName(propertyName);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5897866bc65f5834dab1f17371daada7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,80 @@
using Newtonsoft.Json.Serialization;
using System.Collections.Generic;
namespace AssetStoreTools.Api.Models
{
internal class Package
{
public string PackageId { get; set; }
public string VersionId { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public string Category { get; set; }
public bool IsCompleteProject { get; set; }
public string RootGuid { get; set; }
public string RootPath { get; set; }
public string ProjectPath { get; set; }
public string Modified { get; set; }
public string Size { get; set; }
public string IconUrl { get; set; }
public class AssetStorePackageResolver : DefaultContractResolver
{
private static AssetStorePackageResolver _instance;
public static AssetStorePackageResolver Instance => _instance ?? (_instance = new AssetStorePackageResolver());
private Dictionary<string, string> _propertyConversions;
private AssetStorePackageResolver()
{
_propertyConversions = new Dictionary<string, string>()
{
{ nameof(Package.VersionId), "id" },
{ nameof(Package.IsCompleteProject), "is_complete_project" },
{ nameof(Package.RootGuid), "root_guid" },
{ nameof(Package.RootPath), "root_path" },
{ nameof(Package.ProjectPath), "project_path" },
{ nameof(Package.IconUrl), "icon_url" }
};
}
protected override string ResolvePropertyName(string propertyName)
{
if (_propertyConversions.ContainsKey(propertyName))
return _propertyConversions[propertyName];
return base.ResolvePropertyName(propertyName);
}
}
public class CachedPackageResolver : DefaultContractResolver
{
private static CachedPackageResolver _instance;
public static CachedPackageResolver Instance => _instance ?? (_instance = new CachedPackageResolver());
private Dictionary<string, string> _propertyConversion;
private CachedPackageResolver()
{
this.NamingStrategy = new SnakeCaseNamingStrategy();
_propertyConversion = new Dictionary<string, string>()
{
{ nameof(Package.PackageId), "package_id" },
{ nameof(Package.VersionId), "version_id" },
{ nameof(Package.IsCompleteProject), "is_complete_project" },
{ nameof(Package.RootGuid), "root_guid" },
{ nameof(Package.RootPath), "root_path" },
{ nameof(Package.IconUrl), "icon_url" }
};
}
protected override string ResolvePropertyName(string propertyName)
{
if (_propertyConversion.ContainsKey(propertyName))
return _propertyConversion[propertyName];
return base.ResolvePropertyName(propertyName);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7e9f0b99820061b49abf6e8cf544a727
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,41 @@
using Newtonsoft.Json.Serialization;
using System.Collections.Generic;
namespace AssetStoreTools.Api.Models
{
internal class PackageAdditionalData
{
public string Name { get; set; }
public string Status { get; set; }
public string PackageId { get; set; }
public string VersionId { get; set; }
public string CategoryId { get; set; }
public string Modified { get; set; }
public string Size { get; set; }
public class AssetStorePackageResolver : DefaultContractResolver
{
private static AssetStorePackageResolver _instance;
public static AssetStorePackageResolver Instance => _instance ?? (_instance = new AssetStorePackageResolver());
private Dictionary<string, string> _propertyConversions;
private AssetStorePackageResolver()
{
_propertyConversions = new Dictionary<string, string>()
{
{ nameof(PackageAdditionalData.PackageId), "id" },
{ nameof(PackageAdditionalData.CategoryId), "category_id" }
};
}
protected override string ResolvePropertyName(string propertyName)
{
if (_propertyConversions.ContainsKey(propertyName))
return _propertyConversions[propertyName];
return base.ResolvePropertyName(propertyName);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0663f29f3fcd0e34ab77338d1bdbb528
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,51 @@
using Newtonsoft.Json.Serialization;
using System.Collections.Generic;
namespace AssetStoreTools.Api.Models
{
internal class User
{
public string Id { get; set; }
public string SessionId { get; set; }
public string Name { get; set; }
public string Username { get; set; }
public string PublisherId { get; set; }
public bool IsPublisher => !string.IsNullOrEmpty(PublisherId);
public override string ToString()
{
return
$"{nameof(Id)}: {Id}\n" +
$"{nameof(Name)}: {Name}\n" +
$"{nameof(Username)}: {Username}\n" +
$"{nameof(PublisherId)}: {PublisherId}\n" +
$"{nameof(IsPublisher)}: {IsPublisher}\n" +
$"{nameof(SessionId)}: [HIDDEN]";
}
public class AssetStoreUserResolver : DefaultContractResolver
{
private static AssetStoreUserResolver _instance;
public static AssetStoreUserResolver Instance => _instance ?? (_instance = new AssetStoreUserResolver());
private Dictionary<string, string> _propertyConversions;
private AssetStoreUserResolver()
{
_propertyConversions = new Dictionary<string, string>()
{
{ nameof(User.SessionId), "xunitysession" },
{ nameof(User.PublisherId), "publisher" }
};
}
protected override string ResolvePropertyName(string propertyName)
{
if (_propertyConversions.ContainsKey(propertyName))
return _propertyConversions[propertyName];
return base.ResolvePropertyName(propertyName);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: caf38df5cd685a345a1ebec8f7651c93
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 49581213e7b6ca645955cce8ce23ac4b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,45 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
namespace AssetStoreTools.Api.Responses
{
/// <summary>
/// A structure used to return the success outcome and the result of Asset Store API calls
/// </summary>
internal class AssetStoreResponse
{
public bool Success { get; set; } = false;
public bool Cancelled { get; set; } = false;
public Exception Exception { get; set; }
public AssetStoreResponse() { }
public AssetStoreResponse(Exception e) : this()
{
Exception = e;
}
protected void ValidateAssetStoreResponse(string json)
{
var dict = JsonConvert.DeserializeObject<JObject>(json);
if (dict == null)
throw new Exception("Response is empty");
// Some json responses return an error field on error
if (dict.ContainsKey("error"))
{
// Server side error message
// Do not write to console since this is an error that
// is "expected" ie. can be handled by the gui.
throw new Exception(dict.GetValue("error").ToString());
}
// Some json responses return status+message fields instead of an error field. Go figure.
else if (dict.ContainsKey("status") && dict.GetValue("status").ToString() != "ok"
&& dict.ContainsKey("message"))
{
throw new Exception(dict.GetValue("message").ToString());
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ee338db031a0cfb459f7cac7f41a5d75
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,38 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
namespace AssetStoreTools.Api.Responses
{
internal class AssetStoreToolsVersionResponse : AssetStoreResponse
{
public string Version { get; set; }
public AssetStoreToolsVersionResponse() : base() { }
public AssetStoreToolsVersionResponse(Exception e) : base(e) { }
public AssetStoreToolsVersionResponse(string json)
{
try
{
ValidateAssetStoreResponse(json);
ParseVersion(json);
Success = true;
}
catch (Exception e)
{
Success = false;
Exception = e;
}
}
private void ParseVersion(string json)
{
var dict = JsonConvert.DeserializeObject<JObject>(json);
if (!dict.ContainsKey("version"))
throw new Exception("Version was not found");
Version = dict.GetValue("version").ToString();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 40558675926f913478a654350149209e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,74 @@
using AssetStoreTools.Api.Models;
using Newtonsoft.Json;
using System;
using System.Net;
using System.Net.Http;
namespace AssetStoreTools.Api.Responses
{
internal class AuthenticationResponse : AssetStoreResponse
{
public User User { get; set; }
public AuthenticationResponse() : base() { }
public AuthenticationResponse(Exception e) : base(e) { }
public AuthenticationResponse(HttpStatusCode statusCode, HttpRequestException fallbackException)
{
string message;
switch (statusCode)
{
case HttpStatusCode.Unauthorized:
message = "Incorrect email and/or password. Please try again.";
break;
case HttpStatusCode.InternalServerError:
message = "Authentication request failed\nIf you were logging in with your Unity Cloud account, please make sure you are still logged in.\n" +
"This might also be caused by too many invalid login attempts - if that is the case, please try again later.";
break;
default:
Exception = fallbackException;
return;
}
Exception = new Exception(message);
}
public AuthenticationResponse(string json)
{
try
{
ValidateAssetStoreResponse(json);
var serializerSettings = new JsonSerializerSettings()
{
ContractResolver = User.AssetStoreUserResolver.Instance
};
User = JsonConvert.DeserializeObject<User>(json, serializerSettings);
ValidateLoginData();
ValidatePublisher();
Success = true;
}
catch (Exception e)
{
Success = false;
Exception = e;
}
}
private void ValidateLoginData()
{
if (string.IsNullOrEmpty(User.Id)
|| string.IsNullOrEmpty(User.SessionId)
|| string.IsNullOrEmpty(User.Name)
|| string.IsNullOrEmpty(User.Username))
throw new Exception("Could not parse the necessary publisher information from the response.");
}
private void ValidatePublisher()
{
if (!User.IsPublisher)
throw new Exception($"Your Unity ID {User.Name} is not currently connected to a publisher account. " +
$"Please create a publisher profile.");
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec3a5cb59a7e78646b07f800d317874d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,43 @@
using AssetStoreTools.Api.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
namespace AssetStoreTools.Api.Responses
{
internal class CategoryDataResponse : AssetStoreResponse
{
public List<Category> Categories { get; set; }
public CategoryDataResponse() : base() { }
public CategoryDataResponse(Exception e) : base(e) { }
public CategoryDataResponse(string json)
{
try
{
var categoryArray = JsonConvert.DeserializeObject<JArray>(json);
Categories = new List<Category>();
var serializer = new JsonSerializer()
{
ContractResolver = new Category.AssetStoreCategoryResolver()
};
foreach (var categoryData in categoryArray)
{
var category = categoryData.ToObject<Category>(serializer);
Categories.Add(category);
}
Success = true;
}
catch (Exception e)
{
Success = false;
Exception = e;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e3789323453f1604286b436f77bdca97
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,31 @@
using System;
using UnityEngine;
namespace AssetStoreTools.Api.Responses
{
internal class PackageThumbnailResponse : AssetStoreResponse
{
public Texture2D Thumbnail { get; set; }
public PackageThumbnailResponse() : base() { }
public PackageThumbnailResponse(Exception e) : base(e) { }
public PackageThumbnailResponse(byte[] textureBytes)
{
try
{
var tex = new Texture2D(1, 1, TextureFormat.RGBA32, false);
var success = tex.LoadImage(textureBytes);
if (!success)
throw new Exception("Could not retrieve image from the provided texture bytes");
Thumbnail = tex;
Success = true;
}
catch (Exception e)
{
Success = false;
Exception = e;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dacfba636b3757e408514b850d715e18
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,44 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
namespace AssetStoreTools.Api.Responses
{
internal class PackageUploadedUnityVersionDataResponse : AssetStoreResponse
{
public List<string> UnityVersions { get; set; }
public PackageUploadedUnityVersionDataResponse() : base() { }
public PackageUploadedUnityVersionDataResponse(Exception e) : base(e) { }
public PackageUploadedUnityVersionDataResponse(string json)
{
try
{
ValidateAssetStoreResponse(json);
ParseVersionData(json);
Success = true;
}
catch (Exception e)
{
Success = false;
Exception = e;
}
}
private void ParseVersionData(string json)
{
var data = JsonConvert.DeserializeObject<JObject>(json);
try
{
var content = data.GetValue("content").ToObject<JObject>();
UnityVersions = content.GetValue("unity_versions").ToObject<List<string>>();
}
catch
{
throw new Exception("Could not parse the unity versions array");
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2552f659a600e124aa952f3ba760ddf3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,59 @@
using AssetStoreTools.Api.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
namespace AssetStoreTools.Api.Responses
{
internal class PackagesAdditionalDataResponse : AssetStoreResponse
{
public List<PackageAdditionalData> Packages { get; set; }
public PackagesAdditionalDataResponse() : base() { }
public PackagesAdditionalDataResponse(Exception e) : base(e) { }
public PackagesAdditionalDataResponse(string json)
{
try
{
ValidateAssetStoreResponse(json);
ParseExtraData(json);
Success = true;
}
catch (Exception e)
{
Success = false;
Exception = e;
}
}
private void ParseExtraData(string json)
{
var packageDict = JsonConvert.DeserializeObject<JObject>(json);
if (!packageDict.ContainsKey("packages"))
throw new Exception("Response did not not contain the list of packages");
Packages = new List<PackageAdditionalData>();
var serializer = new JsonSerializer()
{
ContractResolver = PackageAdditionalData.AssetStorePackageResolver.Instance
};
var packageArray = packageDict.GetValue("packages").ToObject<JArray>();
foreach (var packageData in packageArray)
{
var package = packageData.ToObject<PackageAdditionalData>(serializer);
// Some fields are based on the latest version in the json
var latestVersion = packageData["versions"].ToObject<JArray>().Last;
package.VersionId = latestVersion["id"].ToString();
package.Modified = latestVersion["modified"].ToString();
package.Size = latestVersion["size"].ToString();
Packages.Add(package);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88d58ad5e0eea6345b5c83f30ee8ebd5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show More