A game about forced loneliness, made by TACStudios
1using System.Collections.Generic;
2using System.Linq;
3
4using Codice.Client.Common;
5using Codice.LogWrapper;
6using Unity.PlasticSCM.Editor.UI;
7
8namespace Unity.PlasticSCM.Editor.AssetUtils.Processor
9{
10 class AssetPostprocessor : UnityEditor.AssetPostprocessor
11 {
12 internal struct PathToMove
13 {
14 internal readonly string SrcPath;
15 internal readonly string DstPath;
16
17 internal PathToMove(string srcPath, string dstPath)
18 {
19 SrcPath = srcPath;
20 DstPath = dstPath;
21 }
22 }
23
24 internal static bool IsAutomaticAddEnabled { get; private set; }
25
26 static AssetPostprocessor()
27 {
28 IsAutomaticAddEnabled = BoolSetting.Load(
29 UnityConstants.AUTOMATIC_ADD_KEY_NAME, true);
30 }
31
32 internal static void Enable(
33 string wkPath,
34 PlasticAssetsProcessor plasticAssetsProcessor)
35 {
36 mLog.Debug("Enable");
37
38 mWkPath = wkPath;
39 mPlasticAssetsProcessor = plasticAssetsProcessor;
40
41 mIsEnabled = true;
42 }
43
44 internal static void Disable()
45 {
46 mLog.Debug("Disable");
47
48 mIsEnabled = false;
49
50 mWkPath = null;
51 mPlasticAssetsProcessor = null;
52 }
53
54 internal static void SetAutomaticAddPreference(bool isEnabled)
55 {
56 if (IsAutomaticAddEnabled == isEnabled)
57 return;
58
59 IsAutomaticAddEnabled = isEnabled;
60
61 BoolSetting.Save(isEnabled, UnityConstants.AUTOMATIC_ADD_KEY_NAME);
62 }
63
64 internal static void SetIsRepaintNeededAfterAssetDatabaseRefresh()
65 {
66 mIsRepaintNeededAfterAssetDatabaseRefresh = true;
67 }
68
69 static void OnPostprocessAllAssets(
70 string[] importedAssets,
71 string[] deletedAssets,
72 string[] movedAssets,
73 string[] movedFromAssetPaths)
74 {
75 if (!mIsEnabled)
76 return;
77
78 if (mIsRepaintNeededAfterAssetDatabaseRefresh)
79 {
80 mIsRepaintNeededAfterAssetDatabaseRefresh = false;
81
82 ProjectWindow.Repaint();
83 RepaintInspector.All();
84 }
85
86 // Ensure that the MonoFSWatcher is enabled before processing Plastic operations.
87 // It fixes the following scenario:
88 // 1. Close PlasticSCM window.
89 // 2. Create an asset, it appears with the added overlay.
90 // 3. Open PlasticSCM window, the asset should appear as added instead of deleted locally.
91 PlasticApp.EnableMonoFsWatcherIfNeeded();
92
93 List<PathToMove> pathsToMove = GetPathsToMoveOnWorkspace(
94 mWkPath, movedAssets, movedFromAssetPaths);
95
96 mPlasticAssetsProcessor.MoveOnSourceControl(pathsToMove);
97
98 List<string> pathsToCheckout;
99 List<string> pathsToAdd;
100 GetPathsToCheckoutOrAddOnWorkspace(
101 mWkPath, importedAssets, pathsToMove, IsAutomaticAddEnabled,
102 out pathsToCheckout, out pathsToAdd);
103
104 mPlasticAssetsProcessor.CheckoutOnSourceControl(pathsToCheckout);
105
106 mPlasticAssetsProcessor.DeleteFromSourceControl(
107 GetPathsContainedOnWorkspace(mWkPath, deletedAssets));
108
109 mPlasticAssetsProcessor.AddToSourceControl(pathsToAdd);
110
111 // Unity Editor notifies the modified assets through
112 // AssetModificationProcessor.OnWillSaveAssets before getting here.
113 // Known Issue: renamed prefabs not triggering OnWillSaveAssets.
114 mPlasticAssetsProcessor.CheckoutOnSourceControl(
115 GetPathsContainedOnWorkspace(
116 mWkPath, AssetModificationProcessor.ExtractModifiedAssetsToProcess()));
117 }
118
119 static List<PathToMove> GetPathsToMoveOnWorkspace(
120 string wkPath,
121 string[] movedAssets,
122 string[] movedFromAssetPaths)
123 {
124 List<PathToMove> proposedPathsToMove = GetPathsToMoveContainedOnWorkspace(
125 wkPath, movedAssets, movedFromAssetPaths);
126
127 // Unity Editor does not notify the moved assets in order. So, we have
128 // to process the move operations in hierarchical order, otherwise, that
129 // will end up with locally moved paths. This also avoids unnecessary
130 // move operations for children when their parents are also moved.
131
132 proposedPathsToMove.Sort((x, y) => PathHelper.
133 GetPathMatchSorter().Compare(x.SrcPath, y.SrcPath));
134
135 List<PathToMove> result = new List<PathToMove>();
136
137 foreach (PathToMove proposedPathToMove in proposedPathsToMove)
138 {
139 if (result.Any(pathToMove => PathHelper.
140 IsContainedOn(proposedPathToMove.SrcPath, pathToMove.SrcPath)))
141 continue;
142
143 result.Add(proposedPathToMove);
144 }
145
146 return result;
147 }
148
149 static void GetPathsToCheckoutOrAddOnWorkspace(
150 string wkPath,
151 string[] importedAssets,
152 List<PathToMove> pathsToMove,
153 bool isAutomaticAddEnabled,
154 out List<string> pathsToCheckout,
155 out List<string> pathsToAdd)
156 {
157 pathsToCheckout = new List<string>();
158 pathsToAdd = new List<string>();
159
160 // Unity Editor notifies as imported assets a combination of moved+modified
161 // assets and added assets. To ensure proper version control operations,
162 // we need to categorize them accordingly:
163 // • moved+modified assets → check-out them to handle as controlled changes.
164 // • added assets → add them when the automatic add preference is enabled.
165
166 foreach (string asset in importedAssets)
167 {
168 string fullPath = AssetsPath.GetFullPathUnderWorkspace.
169 ForAsset(wkPath, asset);
170
171 if (fullPath == null)
172 continue;
173
174 if (pathsToMove.FindIndex(pathToMove =>
175 pathToMove.DstPath.Equals(fullPath)) != -1)
176 {
177 pathsToCheckout.Add(fullPath);
178 continue;
179 }
180
181 if (isAutomaticAddEnabled)
182 pathsToAdd.Add(fullPath);
183 }
184 }
185
186 static List<PathToMove> GetPathsToMoveContainedOnWorkspace(
187 string wkPath,
188 string[] movedAssets,
189 string[] movedFromAssetPaths)
190 {
191 List<PathToMove> result = new List<PathToMove>(movedAssets.Length);
192
193 for (int i = 0; i < movedAssets.Length; i++)
194 {
195 string fullSrcPath = AssetsPath.GetFullPathUnderWorkspace.
196 ForAsset(wkPath, movedFromAssetPaths[i]);
197
198 if (fullSrcPath == null)
199 continue;
200
201 string fullDstPath = AssetsPath.GetFullPathUnderWorkspace.
202 ForAsset(wkPath, movedAssets[i]);
203
204 if (fullDstPath == null)
205 continue;
206
207 result.Add(new PathToMove(
208 fullSrcPath, fullDstPath));
209 }
210
211 return result;
212 }
213
214 static List<string> GetPathsContainedOnWorkspace(
215 string wkPath, string[] assets)
216 {
217 List<string> result = new List<string>(
218 assets.Length);
219
220 foreach (string asset in assets)
221 {
222 string fullPath = AssetsPath.GetFullPathUnderWorkspace.
223 ForAsset(wkPath, asset);
224
225 if (fullPath == null)
226 continue;
227
228 result.Add(fullPath);
229 }
230
231 return result;
232 }
233
234 static bool mIsEnabled;
235 static bool mIsRepaintNeededAfterAssetDatabaseRefresh;
236
237 static PlasticAssetsProcessor mPlasticAssetsProcessor;
238 static string mWkPath;
239
240 static readonly ILog mLog = PlasticApp.GetLogger("AssetPostprocessor");
241 }
242}