I’ll be answering my own question here, but after sleeping a bit on the problem a solution came to my mind today. The problem is apparently that when I create a new folder using AssetDatabase::CreateFolder, the folder is not available right away in the database. Instead it’s queued for a later import.
So what I do now is to try moving my asset as I did before, but if it fails, I put the move operation on hold for a while, by storing the source and destination paths in a list somewhere. Then in my OnPostProcessAllAssets callback, on monitor not only the assets I’m actually interested in processing, but also the folders that I created in previous calls. When the folder I created before is actually imported and gets through OnPostprocessAllAssets, I finally perform the pending moves which involve it. Here’s how it looks :
static private List<KeyValuePair<string, string>> delayedMoves = new List<KeyValuePair<string, string>>();
static private void OnPostprocessAllAssets (
string[] importedAssets,
string[] deletedAssets,
string[] movedAssets,
string[] movedFromAssetPaths)
{
// The processing of deleted asset is stripped in this snippet.
foreach (string newPath in importedAssets) {
// The part related to delayed move operations
if (delayedMoves.Count > 0) {
MaybePerformDelayedMoves(newPath);
}
// The part related to what I really want to do to assets
if (IsValidTextfilePath(newPath)) {
newOrUpdatedTextfiles.Add(newPath);
}
}
// This is the stripped version of the asset moving code.
for (int i=0; i<movedAssets.Length; i++) {
string newTextfilePath = movedAssets*;*
_ string oldTextfilePath = movedFromAssetPaths*;_
_ if (SomeConditionOnPaths()) {_
_ // TextfilePathToDialogPath computes a subfolder name where the dialog asset to move must go._
_ string newDialogPath = TextfilePathToDialogPath(newTextfilePath, false);_
_ Util.CreateDirectories(newDialogPath, false);_
_ // Nicely ask the database for permission to perform the move right away*_
* string res = AssetDatabase.ValidateMoveAsset(oldAssetPath, newDialogPath);*
* if (res.IsNullOrEmpty()) {*
* // It’s okay, we can move right away*
* AssetDatabase.MoveAsset(oldAssetPath, newDialogPath);*
* } else {*
* // A move fail may be a temporary error, if the destination directory*
* // was just created and is not imported yet. Try later.*
* MoveAssetLater(oldAssetPath, newDialogPath);*
* }*
* }*
* }*
}
static private void MoveAssetLater(string from, string to) {
* delayedMoves.Add(new KeyValuePair<string, string>(from, to));*
}
static private void MaybePerformDelayedMoves(string newAssetName) {
* int nMoves = delayedMoves.Count;*
* // The list is parsed in reverse order to remove elements easily.*
* for (int i = nMoves-1; i>=0; i–) {*
_ KeyValuePair<string, string> dm = delayedMoves*;
string destinationAssetPath = dm.Value;
string destinationFolder = Util.DirectoryFromPath(destinationAssetPath);
if(destinationFolder == newAssetName) {
// Perform move operation and delete origin directory if it is empty.
string originAssetPath = dm.Key;
string res = AssetDatabase.ValidateMoveAsset(originAssetPath, destinationAssetPath);
if (res.IsNullOrEmpty()) {
string originFolder = Util.DirectoryFromPath(originAssetPath);
AssetDatabase.MoveAsset(originAssetPath, destinationAssetPath);
} else {
GDebug.LogError(string.Format(“Unable to move {0} to {1} even after waiting for the destination folder to be imported.”, originAssetPath, destinationAssetPath));
}
delayedMoves.RemoveAt(i);
}
}
}*_
// The rest of the class is stripped
}