@atelierai/uco 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/package.json +1 -1
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetConfig.cs +132 -132
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetDependencyResolver.cs +237 -237
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/DependencyResolver/NuGetDownloader.cs +82 -82
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Resource/MenuItems.cs +247 -247
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Resource/ToolGroups.cs +153 -153
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/SystemTool/Skills.Create.SkillBody.cs +155 -155
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Console.GetLogs.cs +95 -95
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Docs.HttpFetcher.cs +139 -139
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Instance.ListAll.cs +72 -72
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/PathReadHelper.cs +106 -106
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Script.Validate.cs +252 -252
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Tests/TestResultCollector.cs +537 -537
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/API/Tool/Tests.cs +400 -400
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Branding/ProductInfo.cs +62 -62
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/CopilotServerManager.cs +1034 -1034
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Skills/Skill_InitialSetup.cs +197 -197
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Startup.Editor.cs +201 -201
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Startup.cs +67 -67
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/CopilotListWindowBase.cs +393 -393
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/CopilotWindowBase.cs +113 -113
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.Connection.cs +3 -3
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CopilotFeatures.cs +195 -195
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CopilotServer.cs +20 -20
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.CreateGUI.cs +354 -354
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowEditor.cs +78 -78
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/MainWindowInitializer.cs +47 -47
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UI/Window/SerializationCheckWindow.cs +190 -190
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Build.cs +91 -91
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Config.cs +298 -298
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Static.cs +296 -296
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.cs +69 -69
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/PackageUtils.cs +168 -168
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/PrefsKeyMigration.cs +119 -119
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/SafeDefaultsGuard.cs +180 -180
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/ScriptUtils.cs +229 -229
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/ToolsManifestGenerator.cs +200 -200
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityAuthoringAdapters.cs +1467 -1467
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityAuthoringSafetyReloadGuard.cs +65 -65
- package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/Utils/UnityCopilotUpdateProjectSettings.cs +66 -66
- package/vendor/plugin/com.atelierai.unity.copilot/Plugins/ReflectorNet.dll +0 -0
- package/vendor/plugin/com.atelierai.unity.copilot/Plugins/Uco.Framework.Common.dll +0 -0
- package/vendor/plugin/com.atelierai.unity.copilot/Plugins/Uco.Framework.dll +0 -0
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Converter/Json/Structures/EntityIdConverter.cs +120 -120
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Data/GameObjectMetadata.cs +110 -110
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Data/GameObjectMetadata.pre-Unity.6.5.cs +110 -110
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Unity/Logs/FileLogStorage.cs +547 -547
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.Build.cs +243 -243
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.Config.cs +3 -3
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPlugin.cs +295 -295
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginBuilder.cs +105 -105
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginRuntime.Static.cs +155 -155
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/UnityCopilotPluginRuntime.cs +59 -59
- package/vendor/plugin/com.atelierai.unity.copilot/Runtime/Utils/EnvironmentUtils.cs +11 -11
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AtomicApi/AtomicApiTestFixtures.cs +99 -99
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AtomicApi/TryModifyAtTests.cs +227 -227
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/AuthoringSafetyPilotTests.cs +1050 -1050
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/DependencyResolver/NuGetLegacyMigrationTests.cs +304 -304
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Environment/EnvironmentUtilsTests.cs +1 -1
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/BlacklistTypeTests.cs +416 -416
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataFieldScriptableObjectPopulationTests.cs +172 -172
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataFieldScriptableObjectPopulationTests.pre-Unity.6.5.cs +169 -169
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPopulationTests.cs +182 -182
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPopulationTests.pre-Unity.6.5.cs +179 -179
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyPopulationTests.cs +175 -175
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyPopulationTests.pre-Unity.6.5.cs +174 -174
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyScriptableObjectPopulationTests.cs +171 -171
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ReflectionConverter/DataPropertyScriptableObjectPopulationTests.pre-Unity.6.5.cs +170 -170
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsCreateFolderTests.cs +243 -243
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsMaterialTests.cs +101 -101
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsPrefabCreateTests.cs +348 -348
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsPrefabCreateTests.pre-Unity.6.5.cs +348 -348
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Assets/AssetsShaderTests.cs +194 -194
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/PathBasedToolTests.cs +573 -573
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/PathBasedToolTests.pre-Unity.6.5.cs +513 -513
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotCameraTests.cs +154 -154
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotCameraTests.pre-Unity.6.5.cs +154 -154
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotGameViewTests.cs +164 -164
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/Screenshot/ScreenshotSceneViewTests.cs +155 -155
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolParameterTests.cs +56 -56
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolThreadSafetyTests.cs +651 -651
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/Tool/ToolThreadSafetyTests.pre-Unity.6.5.cs +651 -651
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/ToolBatchExecutionIntegrationTests.cs +407 -407
- package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/{McpPluginTests.cs → UcoPluginTests.cs} +105 -105
- package/vendor/plugin/com.atelierai.unity.copilot/package.json +1 -1
- /package/vendor/plugin/com.atelierai.unity.copilot/Tests/Editor/{McpPluginTests.cs.meta → UcoPluginTests.cs.meta} +0 -0
package/vendor/plugin/com.atelierai.unity.copilot/Editor/Scripts/UnityCopilotPluginEditor.Config.cs
CHANGED
|
@@ -1,298 +1,298 @@
|
|
|
1
|
-
/*
|
|
2
|
-
┌──────────────────────────────────────────────────────────────────┐
|
|
3
|
-
│ Author: Ivan Murzak (https://github.com/IvanMurzak) │
|
|
4
|
-
│ Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) │
|
|
5
|
-
│ Copyright (c) 2025 Ivan Murzak │
|
|
6
|
-
│ Licensed under the Apache License, Version 2.0. │
|
|
7
|
-
│ See the LICENSE file in the project root for more information. │
|
|
8
|
-
└──────────────────────────────────────────────────────────────────┘
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
#nullable enable
|
|
12
|
-
using System;
|
|
13
|
-
using System.IO;
|
|
14
|
-
using System.Linq;
|
|
15
|
-
using System.Text.Json;
|
|
16
|
-
using System.Text.Json.Serialization;
|
|
17
|
-
using com.AtelierAI.Unity.Copilot.Runtime.Utils;
|
|
18
|
-
using Microsoft.Extensions.Logging;
|
|
19
|
-
using UnityEngine;
|
|
20
|
-
|
|
21
|
-
namespace com.AtelierAI.Unity.Copilot
|
|
22
|
-
{
|
|
23
|
-
public partial class UnityCopilotPluginEditor
|
|
24
|
-
{
|
|
25
|
-
public static string ResourcesFileName => "uco-config";
|
|
26
|
-
|
|
27
|
-
/// <summary>
|
|
28
|
-
/// Pre-rename filename (uco 0.3.0 / plugin 0.76.0 renamed
|
|
29
|
-
/// AI-Game-Developer-Config.json to uco-config.json). When only the
|
|
30
|
-
/// legacy file exists it is read and then migrated: saved under the
|
|
31
|
-
/// new name and the legacy file removed.
|
|
32
|
-
/// </summary>
|
|
33
|
-
public static string LegacyResourcesFileName => "AI-Game-Developer-Config";
|
|
34
|
-
|
|
35
|
-
/// <summary>True when only the legacy-named config exists right now.</summary>
|
|
36
|
-
static bool LegacyConfigNeedsMigration
|
|
37
|
-
{
|
|
38
|
-
get
|
|
39
|
-
{
|
|
40
|
-
var newPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{ResourcesFileName}.json"));
|
|
41
|
-
var legacyPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{LegacyResourcesFileName}.json"));
|
|
42
|
-
return !File.Exists(newPath) && File.Exists(legacyPath);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/// <summary>Project-relative path of the config actually present on disk.</summary>
|
|
47
|
-
public static string EffectiveAssetsFilePath
|
|
48
|
-
{
|
|
49
|
-
get
|
|
50
|
-
{
|
|
51
|
-
var newPath = $"UserSettings/{ResourcesFileName}.json";
|
|
52
|
-
var legacyPath = $"UserSettings/{LegacyResourcesFileName}.json";
|
|
53
|
-
if (File.Exists(Path.GetFullPath(Path.Combine(ProjectRootPath, newPath))))
|
|
54
|
-
return newPath;
|
|
55
|
-
return File.Exists(Path.GetFullPath(Path.Combine(ProjectRootPath, legacyPath)))
|
|
56
|
-
? legacyPath
|
|
57
|
-
: newPath;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/// <summary>
|
|
62
|
-
/// One-shot rename migration: after a config was loaded from the
|
|
63
|
-
/// legacy filename, persist it under the new name and delete the old
|
|
64
|
-
/// file so the project converges on UserSettings/uco-config.json.
|
|
65
|
-
/// </summary>
|
|
66
|
-
static void MigrateLegacyConfigFileIfNeeded()
|
|
67
|
-
{
|
|
68
|
-
try
|
|
69
|
-
{
|
|
70
|
-
if (!LegacyConfigNeedsMigration)
|
|
71
|
-
return;
|
|
72
|
-
var legacyPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{LegacyResourcesFileName}.json"));
|
|
73
|
-
var newPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{ResourcesFileName}.json"));
|
|
74
|
-
File.Copy(legacyPath, newPath, overwrite: false);
|
|
75
|
-
File.Delete(legacyPath);
|
|
76
|
-
_logger.LogWarning("{method}: migrated {legacy} to {new} (uco rename); the old file was removed.",
|
|
77
|
-
nameof(MigrateLegacyConfigFileIfNeeded), LegacyResourcesFileName, ResourcesFileName);
|
|
78
|
-
}
|
|
79
|
-
catch (Exception e)
|
|
80
|
-
{
|
|
81
|
-
_logger.LogWarning(e, "{method}: legacy config migration failed; falling back to the legacy name for this session.",
|
|
82
|
-
nameof(MigrateLegacyConfigFileIfNeeded));
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/// <summary>
|
|
87
|
-
/// Project-relative path used by Unity's AssetDatabase API.
|
|
88
|
-
/// Do NOT use this with System.IO File/Directory operations — use <see cref="AssetsFileAbsolutePath"/> instead.
|
|
89
|
-
/// </summary>
|
|
90
|
-
public static string AssetsFilePath => $"UserSettings/{ResourcesFileName}.json";
|
|
91
|
-
|
|
92
|
-
/// <summary>
|
|
93
|
-
/// Gets the Unity project root folder path (the folder that contains the Assets directory).
|
|
94
|
-
/// Uses <see cref="Application.dataPath"/> to ensure correctness regardless of the process working directory.
|
|
95
|
-
/// </summary>
|
|
96
|
-
public static string ProjectRootPath => Path.GetDirectoryName(Application.dataPath)!;
|
|
97
|
-
|
|
98
|
-
/// <summary>
|
|
99
|
-
/// Absolute path to the config file for use with System.IO File/Directory operations.
|
|
100
|
-
/// Built from <see cref="ProjectRootPath"/> and <see cref="AssetsFilePath"/> to keep paths consistent.
|
|
101
|
-
/// </summary>
|
|
102
|
-
public static string AssetsFileAbsolutePath => Path.GetFullPath(Path.Combine(ProjectRootPath, AssetsFilePath));
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
/// <summary>
|
|
106
|
-
/// Resets the config file to its default state.
|
|
107
|
-
/// </summary>
|
|
108
|
-
public static void ResetConfig()
|
|
109
|
-
{
|
|
110
|
-
Instance.unityConnectionConfig.SetDefault();
|
|
111
|
-
var plugin = CurrentPlugin;
|
|
112
|
-
if (plugin != null)
|
|
113
|
-
Instance.
|
|
114
|
-
Instance.Save(captureCurrentToolStates: false);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
#if UNITY_EDITOR
|
|
118
|
-
public static UnityEngine.TextAsset AssetFile => UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.TextAsset>(EffectiveAssetsFilePath);
|
|
119
|
-
#endif
|
|
120
|
-
|
|
121
|
-
UnityConnectionConfig GetOrCreateConfig() => GetOrCreateConfig(out _);
|
|
122
|
-
UnityConnectionConfig GetOrCreateConfig(out bool wasCreated)
|
|
123
|
-
{
|
|
124
|
-
wasCreated = false;
|
|
125
|
-
try
|
|
126
|
-
{
|
|
127
|
-
// Both Edit mode and Play mode read from the same UserSettings JSON file
|
|
128
|
-
// Use the absolute path so File.Exists/ReadAllText resolve correctly regardless of CWD.
|
|
129
|
-
// The effective path prefers the renamed uco-config.json and falls back to
|
|
130
|
-
// the legacy AI-Game-Developer-Config.json until the one-shot migration runs.
|
|
131
|
-
var effectiveAbsolute = Path.GetFullPath(Path.Combine(ProjectRootPath, EffectiveAssetsFilePath));
|
|
132
|
-
var json = File.Exists(effectiveAbsolute) ? File.ReadAllText(effectiveAbsolute) : null;
|
|
133
|
-
if (!string.IsNullOrWhiteSpace(json))
|
|
134
|
-
MigrateLegacyConfigFileIfNeeded();
|
|
135
|
-
|
|
136
|
-
UnityConnectionConfig? config = null;
|
|
137
|
-
try
|
|
138
|
-
{
|
|
139
|
-
config = string.IsNullOrWhiteSpace(json)
|
|
140
|
-
? null
|
|
141
|
-
: JsonSerializer.Deserialize<UnityConnectionConfig>(
|
|
142
|
-
// Legacy 1.0.2-and-earlier configs may carry the removed Cloud
|
|
143
|
-
// connection mode; JsonStringEnumConverter would throw on it.
|
|
144
|
-
json!.Replace("\"connectionMode\": \"Cloud\"", "\"connectionMode\": \"Custom\"")
|
|
145
|
-
.Replace("\"ConnectionMode\": \"Cloud\"", "\"ConnectionMode\": \"Custom\""),
|
|
146
|
-
new JsonSerializerOptions
|
|
147
|
-
{
|
|
148
|
-
PropertyNameCaseInsensitive = true,
|
|
149
|
-
Converters = { new JsonStringEnumConverter() }
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
catch (Exception e)
|
|
153
|
-
{
|
|
154
|
-
_logger.LogCritical(e, "{method}: <color=red><b>{file}</b> file is corrupted at <i>{path}</i></color>",
|
|
155
|
-
nameof(GetOrCreateConfig), ResourcesFileName, AssetsFilePath);
|
|
156
|
-
}
|
|
157
|
-
// Track whether the config originated from a deserialized disk file. The
|
|
158
|
-
// safe-defaults migration below only applies to configs that pre-existed on
|
|
159
|
-
// disk — a freshly created config already starts with the safe defaults.
|
|
160
|
-
var loadedFromDisk = config != null;
|
|
161
|
-
if (config == null)
|
|
162
|
-
{
|
|
163
|
-
_logger.LogWarning("{method}: <color=orange><b>Creating {file}</b> file at <i>{path}</i></color>",
|
|
164
|
-
nameof(GetOrCreateConfig), ResourcesFileName, AssetsFilePath);
|
|
165
|
-
config = new UnityConnectionConfig();
|
|
166
|
-
wasCreated = true;
|
|
167
|
-
}
|
|
168
|
-
if (string.IsNullOrEmpty(config.LocalToken))
|
|
169
|
-
{
|
|
170
|
-
config.LocalToken = GenerateToken();
|
|
171
|
-
wasCreated = true;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// ── Safe-defaults (fail-closed) one-shot migration ─────────────────────────
|
|
175
|
-
//
|
|
176
|
-
// Newly created configs default AllowLanBind/AllowInsecureRemoteHttp to false so
|
|
177
|
-
// a fresh project never starts the server on 0.0.0.0 without explicit consent.
|
|
178
|
-
// However, projects that EXISTED before this change may already have LocalHost set
|
|
179
|
-
// to a non-loopback URL (e.g. 0.0.0.0 or a LAN IP) on purpose. Silently dropping
|
|
180
|
-
// their server start would be a regression. Detect that case here and grant the
|
|
181
|
-
// opt-in once, with a one-time LogWarning so the user knows it happened and can
|
|
182
|
-
// turn it back off if they did not intend it.
|
|
183
|
-
//
|
|
184
|
-
// Triggers ONLY when:
|
|
185
|
-
// • The config was actually loaded from disk (loadedFromDisk == true)
|
|
186
|
-
// • LocalHost parses as a URL with a non-loopback host
|
|
187
|
-
// • AllowLanBind is currently false (default)
|
|
188
|
-
// The migration sets AllowLanBind = true and flags `wasCreated` so the Save() at
|
|
189
|
-
// the end of construction persists the new flag immediately. AllowInsecureRemoteHttp
|
|
190
|
-
// is NOT auto-granted — it gates the configurators (which are advisory) and only
|
|
191
|
-
// affects emitted client configs, not server start, so a one-time warning at start
|
|
192
|
-
// is sufficient there.
|
|
193
|
-
if (loadedFromDisk && !config.AllowLanBind && !string.IsNullOrEmpty(config.LocalHost))
|
|
194
|
-
{
|
|
195
|
-
if (com.AtelierAI.Unity.Copilot.Editor.Utils.SafeDefaultsGuard.IsNonLoopbackUrl(config.LocalHost))
|
|
196
|
-
{
|
|
197
|
-
_logger.LogWarning(
|
|
198
|
-
"{method}: <color=orange>Detected pre-existing LAN/all-interfaces LocalHost ({host}) without AllowLanBind. " +
|
|
199
|
-
"Granting AllowLanBind=true once to preserve current behaviour. " +
|
|
200
|
-
"Open the Game Developer window and turn it off if you did not intend to expose the server to the network.</color>",
|
|
201
|
-
nameof(GetOrCreateConfig), config.LocalHost);
|
|
202
|
-
config.AllowLanBind = true;
|
|
203
|
-
wasCreated = true; // force Save() so the migration persists.
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return config;
|
|
208
|
-
}
|
|
209
|
-
catch (Exception e)
|
|
210
|
-
{
|
|
211
|
-
_logger.LogCritical(e, "{method}: <color=red><b>{file}</b> file can't be loaded from <i>{path}</i></color>",
|
|
212
|
-
nameof(GetOrCreateConfig), ResourcesFileName, AssetsFilePath);
|
|
213
|
-
throw;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
public void Save(bool captureCurrentToolStates = true)
|
|
218
|
-
{
|
|
219
|
-
#if UNITY_EDITOR
|
|
220
|
-
Validate();
|
|
221
|
-
try
|
|
222
|
-
{
|
|
223
|
-
var directory = Path.GetDirectoryName(AssetsFileAbsolutePath);
|
|
224
|
-
if (!Directory.Exists(directory))
|
|
225
|
-
Directory.CreateDirectory(directory!);
|
|
226
|
-
|
|
227
|
-
unityConnectionConfig ??= new UnityConnectionConfig();
|
|
228
|
-
|
|
229
|
-
if (captureCurrentToolStates)
|
|
230
|
-
{
|
|
231
|
-
var enabledToolNames = Tools?.GetAllTools()
|
|
232
|
-
?.Select(t => new UnityConnectionConfig.CopilotFeature(t.Name, Tools.IsToolEnabled(t.Name)))
|
|
233
|
-
?.ToList();
|
|
234
|
-
|
|
235
|
-
var enabledPromptNames = Prompts?.GetAllPrompts()
|
|
236
|
-
?.Select(p => new UnityConnectionConfig.CopilotFeature(p.Name, Prompts.IsPromptEnabled(p.Name)))
|
|
237
|
-
?.ToList();
|
|
238
|
-
|
|
239
|
-
var enabledResourceNames = Resources?.GetAllResources()
|
|
240
|
-
?.Select(r => new UnityConnectionConfig.CopilotFeature(r.Name, Resources.IsResourceEnabled(r.Name)))
|
|
241
|
-
?.ToList();
|
|
242
|
-
|
|
243
|
-
unityConnectionConfig.Tools = enabledToolNames != null && enabledToolNames.Count > 0
|
|
244
|
-
? enabledToolNames
|
|
245
|
-
: UnityConnectionConfig.DefaultTools;
|
|
246
|
-
|
|
247
|
-
unityConnectionConfig.Prompts = enabledPromptNames != null && enabledPromptNames.Count > 0
|
|
248
|
-
? enabledPromptNames
|
|
249
|
-
: UnityConnectionConfig.DefaultPrompts;
|
|
250
|
-
|
|
251
|
-
unityConnectionConfig.Resources = enabledResourceNames != null && enabledResourceNames.Count > 0
|
|
252
|
-
? enabledResourceNames
|
|
253
|
-
: UnityConnectionConfig.DefaultResources;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Runtime-only overrides (env vars / CLI flags) MUST NOT be persisted to disk.
|
|
257
|
-
// Temporarily restore the disk-baseline values for any overridden field, serialize,
|
|
258
|
-
// then re-apply the overrides so the in-memory config keeps its runtime values.
|
|
259
|
-
// The "in-memory keeps overrides" property is critical: callers (UI, configurators,
|
|
260
|
-
// SignalR client) hold references to unityConnectionConfig and continue reading
|
|
261
|
-
// from it after Save returns. Restoring overrides ensures behaviour is unchanged.
|
|
262
|
-
var hasRuntimeOverrides = RuntimeOverrides != null && RuntimeOverrides.HasAny;
|
|
263
|
-
if (hasRuntimeOverrides)
|
|
264
|
-
EnvironmentUtils.ApplyBaseline(unityConnectionConfig, RuntimeOverrides!);
|
|
265
|
-
try
|
|
266
|
-
{
|
|
267
|
-
var json = JsonSerializer.Serialize(unityConnectionConfig, new JsonSerializerOptions
|
|
268
|
-
{
|
|
269
|
-
WriteIndented = true,
|
|
270
|
-
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
271
|
-
Converters = { new JsonStringEnumConverter() }
|
|
272
|
-
});
|
|
273
|
-
File.WriteAllText(AssetsFileAbsolutePath, json);
|
|
274
|
-
}
|
|
275
|
-
finally
|
|
276
|
-
{
|
|
277
|
-
if (hasRuntimeOverrides)
|
|
278
|
-
EnvironmentUtils.ApplyOverrides(unityConnectionConfig, RuntimeOverrides!);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
var assetFile = AssetFile;
|
|
282
|
-
if (assetFile != null)
|
|
283
|
-
UnityEditor.EditorUtility.SetDirty(assetFile);
|
|
284
|
-
else
|
|
285
|
-
UnityEditor.AssetDatabase.ImportAsset(AssetsFilePath);
|
|
286
|
-
}
|
|
287
|
-
catch (Exception e)
|
|
288
|
-
{
|
|
289
|
-
_logger.LogCritical(e, "{method}: <color=red><b>{file}</b> file can't be saved at <i>{path}</i></color>",
|
|
290
|
-
nameof(Save), ResourcesFileName, AssetsFilePath);
|
|
291
|
-
}
|
|
292
|
-
#else
|
|
293
|
-
// do nothing in runtime builds
|
|
294
|
-
return;
|
|
295
|
-
#endif
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
3
|
+
│ Author: Ivan Murzak (https://github.com/IvanMurzak) │
|
|
4
|
+
│ Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) │
|
|
5
|
+
│ Copyright (c) 2025 Ivan Murzak │
|
|
6
|
+
│ Licensed under the Apache License, Version 2.0. │
|
|
7
|
+
│ See the LICENSE file in the project root for more information. │
|
|
8
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#nullable enable
|
|
12
|
+
using System;
|
|
13
|
+
using System.IO;
|
|
14
|
+
using System.Linq;
|
|
15
|
+
using System.Text.Json;
|
|
16
|
+
using System.Text.Json.Serialization;
|
|
17
|
+
using com.AtelierAI.Unity.Copilot.Runtime.Utils;
|
|
18
|
+
using Microsoft.Extensions.Logging;
|
|
19
|
+
using UnityEngine;
|
|
20
|
+
|
|
21
|
+
namespace com.AtelierAI.Unity.Copilot
|
|
22
|
+
{
|
|
23
|
+
public partial class UnityCopilotPluginEditor
|
|
24
|
+
{
|
|
25
|
+
public static string ResourcesFileName => "uco-config";
|
|
26
|
+
|
|
27
|
+
/// <summary>
|
|
28
|
+
/// Pre-rename filename (uco 0.3.0 / plugin 0.76.0 renamed
|
|
29
|
+
/// AI-Game-Developer-Config.json to uco-config.json). When only the
|
|
30
|
+
/// legacy file exists it is read and then migrated: saved under the
|
|
31
|
+
/// new name and the legacy file removed.
|
|
32
|
+
/// </summary>
|
|
33
|
+
public static string LegacyResourcesFileName => "AI-Game-Developer-Config";
|
|
34
|
+
|
|
35
|
+
/// <summary>True when only the legacy-named config exists right now.</summary>
|
|
36
|
+
static bool LegacyConfigNeedsMigration
|
|
37
|
+
{
|
|
38
|
+
get
|
|
39
|
+
{
|
|
40
|
+
var newPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{ResourcesFileName}.json"));
|
|
41
|
+
var legacyPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{LegacyResourcesFileName}.json"));
|
|
42
|
+
return !File.Exists(newPath) && File.Exists(legacyPath);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// <summary>Project-relative path of the config actually present on disk.</summary>
|
|
47
|
+
public static string EffectiveAssetsFilePath
|
|
48
|
+
{
|
|
49
|
+
get
|
|
50
|
+
{
|
|
51
|
+
var newPath = $"UserSettings/{ResourcesFileName}.json";
|
|
52
|
+
var legacyPath = $"UserSettings/{LegacyResourcesFileName}.json";
|
|
53
|
+
if (File.Exists(Path.GetFullPath(Path.Combine(ProjectRootPath, newPath))))
|
|
54
|
+
return newPath;
|
|
55
|
+
return File.Exists(Path.GetFullPath(Path.Combine(ProjectRootPath, legacyPath)))
|
|
56
|
+
? legacyPath
|
|
57
|
+
: newPath;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/// <summary>
|
|
62
|
+
/// One-shot rename migration: after a config was loaded from the
|
|
63
|
+
/// legacy filename, persist it under the new name and delete the old
|
|
64
|
+
/// file so the project converges on UserSettings/uco-config.json.
|
|
65
|
+
/// </summary>
|
|
66
|
+
static void MigrateLegacyConfigFileIfNeeded()
|
|
67
|
+
{
|
|
68
|
+
try
|
|
69
|
+
{
|
|
70
|
+
if (!LegacyConfigNeedsMigration)
|
|
71
|
+
return;
|
|
72
|
+
var legacyPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{LegacyResourcesFileName}.json"));
|
|
73
|
+
var newPath = Path.GetFullPath(Path.Combine(ProjectRootPath, $"UserSettings/{ResourcesFileName}.json"));
|
|
74
|
+
File.Copy(legacyPath, newPath, overwrite: false);
|
|
75
|
+
File.Delete(legacyPath);
|
|
76
|
+
_logger.LogWarning("{method}: migrated {legacy} to {new} (uco rename); the old file was removed.",
|
|
77
|
+
nameof(MigrateLegacyConfigFileIfNeeded), LegacyResourcesFileName, ResourcesFileName);
|
|
78
|
+
}
|
|
79
|
+
catch (Exception e)
|
|
80
|
+
{
|
|
81
|
+
_logger.LogWarning(e, "{method}: legacy config migration failed; falling back to the legacy name for this session.",
|
|
82
|
+
nameof(MigrateLegacyConfigFileIfNeeded));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/// <summary>
|
|
87
|
+
/// Project-relative path used by Unity's AssetDatabase API.
|
|
88
|
+
/// Do NOT use this with System.IO File/Directory operations — use <see cref="AssetsFileAbsolutePath"/> instead.
|
|
89
|
+
/// </summary>
|
|
90
|
+
public static string AssetsFilePath => $"UserSettings/{ResourcesFileName}.json";
|
|
91
|
+
|
|
92
|
+
/// <summary>
|
|
93
|
+
/// Gets the Unity project root folder path (the folder that contains the Assets directory).
|
|
94
|
+
/// Uses <see cref="Application.dataPath"/> to ensure correctness regardless of the process working directory.
|
|
95
|
+
/// </summary>
|
|
96
|
+
public static string ProjectRootPath => Path.GetDirectoryName(Application.dataPath)!;
|
|
97
|
+
|
|
98
|
+
/// <summary>
|
|
99
|
+
/// Absolute path to the config file for use with System.IO File/Directory operations.
|
|
100
|
+
/// Built from <see cref="ProjectRootPath"/> and <see cref="AssetsFilePath"/> to keep paths consistent.
|
|
101
|
+
/// </summary>
|
|
102
|
+
public static string AssetsFileAbsolutePath => Path.GetFullPath(Path.Combine(ProjectRootPath, AssetsFilePath));
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
/// <summary>
|
|
106
|
+
/// Resets the config file to its default state.
|
|
107
|
+
/// </summary>
|
|
108
|
+
public static void ResetConfig()
|
|
109
|
+
{
|
|
110
|
+
Instance.unityConnectionConfig.SetDefault();
|
|
111
|
+
var plugin = CurrentPlugin;
|
|
112
|
+
if (plugin != null)
|
|
113
|
+
Instance.ApplyConfigToUcoPlugin(plugin);
|
|
114
|
+
Instance.Save(captureCurrentToolStates: false);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
#if UNITY_EDITOR
|
|
118
|
+
public static UnityEngine.TextAsset AssetFile => UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.TextAsset>(EffectiveAssetsFilePath);
|
|
119
|
+
#endif
|
|
120
|
+
|
|
121
|
+
UnityConnectionConfig GetOrCreateConfig() => GetOrCreateConfig(out _);
|
|
122
|
+
UnityConnectionConfig GetOrCreateConfig(out bool wasCreated)
|
|
123
|
+
{
|
|
124
|
+
wasCreated = false;
|
|
125
|
+
try
|
|
126
|
+
{
|
|
127
|
+
// Both Edit mode and Play mode read from the same UserSettings JSON file
|
|
128
|
+
// Use the absolute path so File.Exists/ReadAllText resolve correctly regardless of CWD.
|
|
129
|
+
// The effective path prefers the renamed uco-config.json and falls back to
|
|
130
|
+
// the legacy AI-Game-Developer-Config.json until the one-shot migration runs.
|
|
131
|
+
var effectiveAbsolute = Path.GetFullPath(Path.Combine(ProjectRootPath, EffectiveAssetsFilePath));
|
|
132
|
+
var json = File.Exists(effectiveAbsolute) ? File.ReadAllText(effectiveAbsolute) : null;
|
|
133
|
+
if (!string.IsNullOrWhiteSpace(json))
|
|
134
|
+
MigrateLegacyConfigFileIfNeeded();
|
|
135
|
+
|
|
136
|
+
UnityConnectionConfig? config = null;
|
|
137
|
+
try
|
|
138
|
+
{
|
|
139
|
+
config = string.IsNullOrWhiteSpace(json)
|
|
140
|
+
? null
|
|
141
|
+
: JsonSerializer.Deserialize<UnityConnectionConfig>(
|
|
142
|
+
// Legacy 1.0.2-and-earlier configs may carry the removed Cloud
|
|
143
|
+
// connection mode; JsonStringEnumConverter would throw on it.
|
|
144
|
+
json!.Replace("\"connectionMode\": \"Cloud\"", "\"connectionMode\": \"Custom\"")
|
|
145
|
+
.Replace("\"ConnectionMode\": \"Cloud\"", "\"ConnectionMode\": \"Custom\""),
|
|
146
|
+
new JsonSerializerOptions
|
|
147
|
+
{
|
|
148
|
+
PropertyNameCaseInsensitive = true,
|
|
149
|
+
Converters = { new JsonStringEnumConverter() }
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (Exception e)
|
|
153
|
+
{
|
|
154
|
+
_logger.LogCritical(e, "{method}: <color=red><b>{file}</b> file is corrupted at <i>{path}</i></color>",
|
|
155
|
+
nameof(GetOrCreateConfig), ResourcesFileName, AssetsFilePath);
|
|
156
|
+
}
|
|
157
|
+
// Track whether the config originated from a deserialized disk file. The
|
|
158
|
+
// safe-defaults migration below only applies to configs that pre-existed on
|
|
159
|
+
// disk — a freshly created config already starts with the safe defaults.
|
|
160
|
+
var loadedFromDisk = config != null;
|
|
161
|
+
if (config == null)
|
|
162
|
+
{
|
|
163
|
+
_logger.LogWarning("{method}: <color=orange><b>Creating {file}</b> file at <i>{path}</i></color>",
|
|
164
|
+
nameof(GetOrCreateConfig), ResourcesFileName, AssetsFilePath);
|
|
165
|
+
config = new UnityConnectionConfig();
|
|
166
|
+
wasCreated = true;
|
|
167
|
+
}
|
|
168
|
+
if (string.IsNullOrEmpty(config.LocalToken))
|
|
169
|
+
{
|
|
170
|
+
config.LocalToken = GenerateToken();
|
|
171
|
+
wasCreated = true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Safe-defaults (fail-closed) one-shot migration ─────────────────────────
|
|
175
|
+
//
|
|
176
|
+
// Newly created configs default AllowLanBind/AllowInsecureRemoteHttp to false so
|
|
177
|
+
// a fresh project never starts the server on 0.0.0.0 without explicit consent.
|
|
178
|
+
// However, projects that EXISTED before this change may already have LocalHost set
|
|
179
|
+
// to a non-loopback URL (e.g. 0.0.0.0 or a LAN IP) on purpose. Silently dropping
|
|
180
|
+
// their server start would be a regression. Detect that case here and grant the
|
|
181
|
+
// opt-in once, with a one-time LogWarning so the user knows it happened and can
|
|
182
|
+
// turn it back off if they did not intend it.
|
|
183
|
+
//
|
|
184
|
+
// Triggers ONLY when:
|
|
185
|
+
// • The config was actually loaded from disk (loadedFromDisk == true)
|
|
186
|
+
// • LocalHost parses as a URL with a non-loopback host
|
|
187
|
+
// • AllowLanBind is currently false (default)
|
|
188
|
+
// The migration sets AllowLanBind = true and flags `wasCreated` so the Save() at
|
|
189
|
+
// the end of construction persists the new flag immediately. AllowInsecureRemoteHttp
|
|
190
|
+
// is NOT auto-granted — it gates the configurators (which are advisory) and only
|
|
191
|
+
// affects emitted client configs, not server start, so a one-time warning at start
|
|
192
|
+
// is sufficient there.
|
|
193
|
+
if (loadedFromDisk && !config.AllowLanBind && !string.IsNullOrEmpty(config.LocalHost))
|
|
194
|
+
{
|
|
195
|
+
if (com.AtelierAI.Unity.Copilot.Editor.Utils.SafeDefaultsGuard.IsNonLoopbackUrl(config.LocalHost))
|
|
196
|
+
{
|
|
197
|
+
_logger.LogWarning(
|
|
198
|
+
"{method}: <color=orange>Detected pre-existing LAN/all-interfaces LocalHost ({host}) without AllowLanBind. " +
|
|
199
|
+
"Granting AllowLanBind=true once to preserve current behaviour. " +
|
|
200
|
+
"Open the Game Developer window and turn it off if you did not intend to expose the server to the network.</color>",
|
|
201
|
+
nameof(GetOrCreateConfig), config.LocalHost);
|
|
202
|
+
config.AllowLanBind = true;
|
|
203
|
+
wasCreated = true; // force Save() so the migration persists.
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return config;
|
|
208
|
+
}
|
|
209
|
+
catch (Exception e)
|
|
210
|
+
{
|
|
211
|
+
_logger.LogCritical(e, "{method}: <color=red><b>{file}</b> file can't be loaded from <i>{path}</i></color>",
|
|
212
|
+
nameof(GetOrCreateConfig), ResourcesFileName, AssetsFilePath);
|
|
213
|
+
throw;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
public void Save(bool captureCurrentToolStates = true)
|
|
218
|
+
{
|
|
219
|
+
#if UNITY_EDITOR
|
|
220
|
+
Validate();
|
|
221
|
+
try
|
|
222
|
+
{
|
|
223
|
+
var directory = Path.GetDirectoryName(AssetsFileAbsolutePath);
|
|
224
|
+
if (!Directory.Exists(directory))
|
|
225
|
+
Directory.CreateDirectory(directory!);
|
|
226
|
+
|
|
227
|
+
unityConnectionConfig ??= new UnityConnectionConfig();
|
|
228
|
+
|
|
229
|
+
if (captureCurrentToolStates)
|
|
230
|
+
{
|
|
231
|
+
var enabledToolNames = Tools?.GetAllTools()
|
|
232
|
+
?.Select(t => new UnityConnectionConfig.CopilotFeature(t.Name, Tools.IsToolEnabled(t.Name)))
|
|
233
|
+
?.ToList();
|
|
234
|
+
|
|
235
|
+
var enabledPromptNames = Prompts?.GetAllPrompts()
|
|
236
|
+
?.Select(p => new UnityConnectionConfig.CopilotFeature(p.Name, Prompts.IsPromptEnabled(p.Name)))
|
|
237
|
+
?.ToList();
|
|
238
|
+
|
|
239
|
+
var enabledResourceNames = Resources?.GetAllResources()
|
|
240
|
+
?.Select(r => new UnityConnectionConfig.CopilotFeature(r.Name, Resources.IsResourceEnabled(r.Name)))
|
|
241
|
+
?.ToList();
|
|
242
|
+
|
|
243
|
+
unityConnectionConfig.Tools = enabledToolNames != null && enabledToolNames.Count > 0
|
|
244
|
+
? enabledToolNames
|
|
245
|
+
: UnityConnectionConfig.DefaultTools;
|
|
246
|
+
|
|
247
|
+
unityConnectionConfig.Prompts = enabledPromptNames != null && enabledPromptNames.Count > 0
|
|
248
|
+
? enabledPromptNames
|
|
249
|
+
: UnityConnectionConfig.DefaultPrompts;
|
|
250
|
+
|
|
251
|
+
unityConnectionConfig.Resources = enabledResourceNames != null && enabledResourceNames.Count > 0
|
|
252
|
+
? enabledResourceNames
|
|
253
|
+
: UnityConnectionConfig.DefaultResources;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Runtime-only overrides (env vars / CLI flags) MUST NOT be persisted to disk.
|
|
257
|
+
// Temporarily restore the disk-baseline values for any overridden field, serialize,
|
|
258
|
+
// then re-apply the overrides so the in-memory config keeps its runtime values.
|
|
259
|
+
// The "in-memory keeps overrides" property is critical: callers (UI, configurators,
|
|
260
|
+
// SignalR client) hold references to unityConnectionConfig and continue reading
|
|
261
|
+
// from it after Save returns. Restoring overrides ensures behaviour is unchanged.
|
|
262
|
+
var hasRuntimeOverrides = RuntimeOverrides != null && RuntimeOverrides.HasAny;
|
|
263
|
+
if (hasRuntimeOverrides)
|
|
264
|
+
EnvironmentUtils.ApplyBaseline(unityConnectionConfig, RuntimeOverrides!);
|
|
265
|
+
try
|
|
266
|
+
{
|
|
267
|
+
var json = JsonSerializer.Serialize(unityConnectionConfig, new JsonSerializerOptions
|
|
268
|
+
{
|
|
269
|
+
WriteIndented = true,
|
|
270
|
+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
271
|
+
Converters = { new JsonStringEnumConverter() }
|
|
272
|
+
});
|
|
273
|
+
File.WriteAllText(AssetsFileAbsolutePath, json);
|
|
274
|
+
}
|
|
275
|
+
finally
|
|
276
|
+
{
|
|
277
|
+
if (hasRuntimeOverrides)
|
|
278
|
+
EnvironmentUtils.ApplyOverrides(unityConnectionConfig, RuntimeOverrides!);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
var assetFile = AssetFile;
|
|
282
|
+
if (assetFile != null)
|
|
283
|
+
UnityEditor.EditorUtility.SetDirty(assetFile);
|
|
284
|
+
else
|
|
285
|
+
UnityEditor.AssetDatabase.ImportAsset(AssetsFilePath);
|
|
286
|
+
}
|
|
287
|
+
catch (Exception e)
|
|
288
|
+
{
|
|
289
|
+
_logger.LogCritical(e, "{method}: <color=red><b>{file}</b> file can't be saved at <i>{path}</i></color>",
|
|
290
|
+
nameof(Save), ResourcesFileName, AssetsFilePath);
|
|
291
|
+
}
|
|
292
|
+
#else
|
|
293
|
+
// do nothing in runtime builds
|
|
294
|
+
return;
|
|
295
|
+
#endif
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|