[unity] Now detecting and warning when binary or json file has been exported with the opposite ending. Improved loading error reporting behaviour. Closes #1857.

This commit is contained in:
Harald Csaszar 2021-03-16 15:09:04 +01:00
parent 305bb21615
commit 417388d2e7
4 changed files with 126 additions and 38 deletions

View File

@ -625,9 +625,12 @@ namespace Spine.Unity.Editor {
warnings.Add("Missing Skeleton JSON"); warnings.Add("Missing Skeleton JSON");
} else { } else {
var fieldValue = (TextAsset)skeletonJSON.objectReferenceValue; var fieldValue = (TextAsset)skeletonJSON.objectReferenceValue;
string problemDescription = null;
if (!AssetUtility.IsSpineData(fieldValue, out compatibilityProblemInfo)) { if (!AssetUtility.IsSpineData(fieldValue, out compatibilityProblemInfo, ref problemDescription)) {
warnings.Add("Skeleton data file is not a valid Spine JSON or binary file."); if (problemDescription != null)
warnings.Add(problemDescription);
else
warnings.Add("Skeleton data file is not a valid Spine JSON or binary file.");
} else { } else {
#if SPINE_TK2D #if SPINE_TK2D
bool searchForSpineAtlasAssets = true; bool searchForSpineAtlasAssets = true;
@ -644,11 +647,12 @@ namespace Spine.Unity.Editor {
var actualAtlasAssets = targetSkeletonDataAsset.atlasAssets; var actualAtlasAssets = targetSkeletonDataAsset.atlasAssets;
for (int i = 0; i < actualAtlasAssets.Length; i++) { for (int i = 0; i < actualAtlasAssets.Length; i++) {
if (targetSkeletonDataAsset.atlasAssets[i] == null) { if (actualAtlasAssets[i] == null) {
detectedNullAtlasEntry = true; detectedNullAtlasEntry = true;
break; break;
} else { } else {
atlasList.Add(actualAtlasAssets[i].GetAtlas()); if (actualAtlasAssets[i].MaterialCount > 0)
atlasList.Add(actualAtlasAssets[i].GetAtlas());
} }
} }
@ -658,8 +662,9 @@ namespace Spine.Unity.Editor {
List<string> missingPaths = null; List<string> missingPaths = null;
if (atlasAssets.arraySize > 0) { if (atlasAssets.arraySize > 0) {
missingPaths = AssetUtility.GetRequiredAtlasRegions(AssetDatabase.GetAssetPath(skeletonJSON.objectReferenceValue)); missingPaths = AssetUtility.GetRequiredAtlasRegions(AssetDatabase.GetAssetPath(skeletonJSON.objectReferenceValue));
foreach (var atlas in atlasList) { foreach (var atlas in atlasList) {
if (atlas == null)
continue;
for (int i = 0; i < missingPaths.Count; i++) { for (int i = 0; i < missingPaths.Count; i++) {
if (atlas.FindRegion(missingPaths[i]) != null) { if (atlas.FindRegion(missingPaths[i]) != null) {
missingPaths.RemoveAt(i); missingPaths.RemoveAt(i);

View File

@ -58,7 +58,18 @@ using System.Reflection;
using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo; using CompatibilityProblemInfo = Spine.Unity.SkeletonDataCompatibility.CompatibilityProblemInfo;
namespace Spine.Unity.Editor { namespace Spine.Unity.Editor {
using PathAndProblemInfo = System.Collections.Generic.KeyValuePair<string, CompatibilityProblemInfo>;
public class PathAndProblemInfo {
public string path;
public CompatibilityProblemInfo compatibilityProblems;
public string otherProblemDescription;
public PathAndProblemInfo (string path, CompatibilityProblemInfo compatibilityInfo, string otherProblemDescription) {
this.path = path;
this.compatibilityProblems = compatibilityInfo;
this.otherProblemDescription = otherProblemDescription;
}
}
public static class AssetUtility { public static class AssetUtility {
@ -276,17 +287,26 @@ namespace Spine.Unity.Editor {
case ".jpg": case ".jpg":
imagePaths.Add(str); imagePaths.Add(str);
break; break;
case ".json": case ".json": {
var jsonAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(str); var jsonAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(str);
if (jsonAsset != null && IsSpineData(jsonAsset, out compatibilityProblemInfo)) string problemDescription = null;
skeletonPaths.Add(new PathAndProblemInfo(str, compatibilityProblemInfo)); if (jsonAsset != null && IsSpineData(jsonAsset, out compatibilityProblemInfo, ref problemDescription))
skeletonPaths.Add(new PathAndProblemInfo(str, compatibilityProblemInfo, problemDescription));
if (problemDescription != null)
Debug.LogError(problemDescription, jsonAsset);
break; break;
case ".bytes": }
case ".bytes": {
if (str.ToLower().EndsWith(".skel.bytes", System.StringComparison.Ordinal)) { if (str.ToLower().EndsWith(".skel.bytes", System.StringComparison.Ordinal)) {
if (IsSpineData(AssetDatabase.LoadAssetAtPath<TextAsset>(str), out compatibilityProblemInfo)) var binaryAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(str);
skeletonPaths.Add(new PathAndProblemInfo(str, compatibilityProblemInfo)); string problemDescription = null;
if (IsSpineData(binaryAsset, out compatibilityProblemInfo, ref problemDescription))
skeletonPaths.Add(new PathAndProblemInfo(str, compatibilityProblemInfo, problemDescription));
if (problemDescription != null)
Debug.LogError(problemDescription, binaryAsset);
} }
break; break;
}
} }
} }
@ -304,8 +324,9 @@ namespace Spine.Unity.Editor {
// Import skeletons and match them with atlases. // Import skeletons and match them with atlases.
bool abortSkeletonImport = false; bool abortSkeletonImport = false;
foreach (var skeletonPathEntry in skeletonPaths) { foreach (var skeletonPathEntry in skeletonPaths) {
string skeletonPath = skeletonPathEntry.Key; string skeletonPath = skeletonPathEntry.path;
var compatibilityProblems = skeletonPathEntry.Value; var compatibilityProblems = skeletonPathEntry.compatibilityProblems;
string otherProblemDescription = skeletonPathEntry.otherProblemDescription;
if (skeletonPath.StartsWith("Packages")) if (skeletonPath.StartsWith("Packages"))
continue; continue;
if (!reimport && CheckForValidSkeletonData(skeletonPath)) { if (!reimport && CheckForValidSkeletonData(skeletonPath)) {
@ -318,6 +339,9 @@ namespace Spine.Unity.Editor {
IngestIncompatibleSpineProject(loadedAsset, compatibilityProblems); IngestIncompatibleSpineProject(loadedAsset, compatibilityProblems);
continue; continue;
} }
if (otherProblemDescription != null) {
continue;
}
string dir = Path.GetDirectoryName(skeletonPath).Replace('\\', '/'); string dir = Path.GetDirectoryName(skeletonPath).Replace('\\', '/');
@ -378,8 +402,12 @@ namespace Spine.Unity.Editor {
if (usedSkeletonPath == null) if (usedSkeletonPath == null)
continue; continue;
if (skeletonPaths.FindIndex(p => { return p.Key == usedSkeletonPath; } ) < 0) { if (skeletonPaths.FindIndex(p => { return p.path == usedSkeletonPath; } ) < 0) {
skeletonPaths.Add(new PathAndProblemInfo(usedSkeletonPath, null)); string problemDescription = null;
CompatibilityProblemInfo compatibilityProblemInfo = null;
TextAsset textAsset = AssetDatabase.LoadAssetAtPath<TextAsset>(usedSkeletonPath);
if (textAsset != null && IsSpineData(textAsset, out compatibilityProblemInfo, ref problemDescription))
skeletonPaths.Add(new PathAndProblemInfo(usedSkeletonPath, compatibilityProblemInfo, problemDescription));
} }
} }
} }
@ -427,7 +455,8 @@ namespace Spine.Unity.Editor {
} }
SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(true); SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(true);
BlendModeMaterialsUtility.UpdateBlendModeMaterials(skeletonDataAsset, ref skeletonData); if (skeletonData != null)
BlendModeMaterialsUtility.UpdateBlendModeMaterials(skeletonDataAsset, ref skeletonData);
string currentHash = skeletonData != null ? skeletonData.Hash : null; string currentHash = skeletonData != null ? skeletonData.Hash : null;
@ -834,11 +863,10 @@ namespace Spine.Unity.Editor {
internal static SkeletonDataAsset IngestIncompatibleSpineProject(TextAsset spineJson, internal static SkeletonDataAsset IngestIncompatibleSpineProject(TextAsset spineJson,
CompatibilityProblemInfo compatibilityProblemInfo) { CompatibilityProblemInfo compatibilityProblemInfo) {
string filePath = GetSkeletonDataAssetFilePath(spineJson);
if (spineJson == null) if (spineJson == null)
return null; return null;
string filePath = GetSkeletonDataAssetFilePath(spineJson);
SkeletonDataAsset skeletonDataAsset = (SkeletonDataAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(SkeletonDataAsset)); SkeletonDataAsset skeletonDataAsset = (SkeletonDataAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(SkeletonDataAsset));
if (skeletonDataAsset == null) { if (skeletonDataAsset == null) {
skeletonDataAsset = SkeletonDataAsset.CreateInstance<SkeletonDataAsset>(); skeletonDataAsset = SkeletonDataAsset.CreateInstance<SkeletonDataAsset>();
@ -924,14 +952,14 @@ namespace Spine.Unity.Editor {
if (skeletonDataAsset != null && skeletonDataAsset.skeletonJSON == textAsset) if (skeletonDataAsset != null && skeletonDataAsset.skeletonJSON == textAsset)
return true; return true;
} }
return false; return false;
} }
public static bool IsSpineData (TextAsset asset, out CompatibilityProblemInfo compatibilityProblemInfo) { public static bool IsSpineData (TextAsset asset, out CompatibilityProblemInfo compatibilityProblemInfo, ref string problemDescription) {
SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(asset); bool isSpineSkeletonData;
SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(asset, out isSpineSkeletonData, ref problemDescription);
compatibilityProblemInfo = SkeletonDataCompatibility.GetCompatibilityProblemInfo(fileVersion); compatibilityProblemInfo = SkeletonDataCompatibility.GetCompatibilityProblemInfo(fileVersion);
return fileVersion != null; return isSpineSkeletonData;
} }
#endregion #endregion

View File

@ -163,22 +163,29 @@ namespace Spine.Unity {
} }
#endif #endif
bool isBinary = skeletonJSON.name.ToLower().Contains(".skel"); bool hasBinaryExtension = skeletonJSON.name.ToLower().Contains(".skel");
SkeletonData loadedSkeletonData = null; SkeletonData loadedSkeletonData = null;
try { try {
if (isBinary) if (hasBinaryExtension)
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.bytes, attachmentLoader, skeletonDataScale); loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.bytes, attachmentLoader, skeletonDataScale);
else else
loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.text, attachmentLoader, skeletonDataScale); loadedSkeletonData = SkeletonDataAsset.ReadSkeletonData(skeletonJSON.text, attachmentLoader, skeletonDataScale);
} catch (Exception ex) { } catch (Exception ex) {
if (!quiet) if (!quiet)
Debug.LogError("Error reading skeleton JSON file for SkeletonData asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, this); Debug.LogError("Error reading skeleton JSON file for SkeletonData asset: " + name + "\n" + ex.Message + "\n" + ex.StackTrace, skeletonJSON);
} }
#if UNITY_EDITOR #if UNITY_EDITOR
if (loadedSkeletonData == null && !quiet && skeletonJSON != null) { if (loadedSkeletonData == null && !quiet && skeletonJSON != null) {
SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(skeletonJSON); string problemDescription = null;
bool isSpineSkeletonData;
SkeletonDataCompatibility.VersionInfo fileVersion = SkeletonDataCompatibility.GetVersionInfo(skeletonJSON, out isSpineSkeletonData, ref problemDescription);
if (problemDescription != null) {
if (!quiet)
Debug.LogError(problemDescription, skeletonJSON);
return null;
}
CompatibilityProblemInfo compatibilityProblemInfo = SkeletonDataCompatibility.GetCompatibilityProblemInfo(fileVersion); CompatibilityProblemInfo compatibilityProblemInfo = SkeletonDataCompatibility.GetCompatibilityProblemInfo(fileVersion);
if (compatibilityProblemInfo != null) { if (compatibilityProblemInfo != null) {
SkeletonDataCompatibility.DisplayCompatibilityProblem(compatibilityProblemInfo.DescriptionString(), skeletonJSON); SkeletonDataCompatibility.DisplayCompatibilityProblem(compatibilityProblemInfo.DescriptionString(), skeletonJSON);
@ -250,7 +257,6 @@ namespace Spine.Unity {
}; };
return json.ReadSkeletonData(input); return json.ReadSkeletonData(input);
} }
} }
} }

View File

@ -30,6 +30,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using UnityEngine; using UnityEngine;
using System;
#if UNITY_EDITOR #if UNITY_EDITOR
using System.Globalization; using System.Globalization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -63,8 +64,12 @@ namespace Spine.Unity {
public class CompatibilityProblemInfo { public class CompatibilityProblemInfo {
public VersionInfo actualVersion; public VersionInfo actualVersion;
public int[][] compatibleVersions; public int[][] compatibleVersions;
public string explicitProblemDescription = null;
public string DescriptionString () { public string DescriptionString () {
if (!string.IsNullOrEmpty(explicitProblemDescription))
return explicitProblemDescription;
string compatibleVersionString = ""; string compatibleVersionString = "";
string optionalOr = null; string optionalOr = null;
foreach (int[] version in compatibleVersions) { foreach (int[] version in compatibleVersions) {
@ -77,12 +82,28 @@ namespace Spine.Unity {
} }
#if UNITY_EDITOR #if UNITY_EDITOR
public static VersionInfo GetVersionInfo (TextAsset asset) { public static VersionInfo GetVersionInfo (TextAsset asset, out bool isSpineSkeletonData, ref string problemDescription) {
isSpineSkeletonData = false;
if (asset == null) if (asset == null)
return null; return null;
VersionInfo fileVersion = new VersionInfo(); VersionInfo fileVersion = new VersionInfo();
fileVersion.sourceType = asset.name.Contains(".skel") ? SourceType.Binary : SourceType.Json; bool hasBinaryExtension = asset.name.Contains(".skel");
fileVersion.sourceType = hasBinaryExtension ? SourceType.Binary : SourceType.Json;
bool isJsonFileByContent = IsJsonFile(asset);
if (hasBinaryExtension == isJsonFileByContent) {
if (hasBinaryExtension) {
problemDescription = string.Format("Failed to read '{0}'. Extension is '.skel.bytes' but content looks like a '.json' file.\n"
+ "Did you choose the wrong extension upon export?\n", asset.name);
}
else {
problemDescription = string.Format("Failed to read '{0}'. Extension is '.json' but content looks like binary 'skel.bytes' file.\n"
+ "Did you choose the wrong extension upon export?\n", asset.name);
}
isSpineSkeletonData = false;
return null;
}
if (fileVersion.sourceType == SourceType.Binary) { if (fileVersion.sourceType == SourceType.Binary) {
try { try {
@ -91,7 +112,8 @@ namespace Spine.Unity {
} }
} }
catch (System.Exception e) { catch (System.Exception e) {
Debug.LogErrorFormat("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e); problemDescription = string.Format("Failed to read '{0}'. It is likely not a binary Spine SkeletonData file.\n{1}", asset.name, e);
isSpineSkeletonData = false;
return null; return null;
} }
} }
@ -103,13 +125,15 @@ namespace Spine.Unity {
else { else {
object obj = Json.Deserialize(new StringReader(asset.text)); object obj = Json.Deserialize(new StringReader(asset.text));
if (obj == null) { if (obj == null) {
Debug.LogErrorFormat("'{0}' is not valid JSON.", asset.name); problemDescription = string.Format("'{0}' is not valid JSON.", asset.name);
isSpineSkeletonData = false;
return null; return null;
} }
var root = obj as Dictionary<string, object>; var root = obj as Dictionary<string, object>;
if (root == null) { if (root == null) {
Debug.LogErrorFormat("'{0}' is not compatible JSON. Parser returned an incorrect type while parsing version info.", asset.name); problemDescription = string.Format("'{0}' is not compatible JSON. Parser returned an incorrect type while parsing version info.", asset.name);
isSpineSkeletonData = false;
return null; return null;
} }
@ -123,7 +147,8 @@ namespace Spine.Unity {
} }
if (string.IsNullOrEmpty(fileVersion.rawVersion)) { if (string.IsNullOrEmpty(fileVersion.rawVersion)) {
// very likely not a Spine skeleton json file at all. // very likely not a Spine skeleton json file at all. Could be another valid json file, don't report errors.
isSpineSkeletonData = false;
return null; return null;
} }
@ -133,15 +158,39 @@ namespace Spine.Unity {
int.Parse(versionSplit[1], CultureInfo.InvariantCulture) }; int.Parse(versionSplit[1], CultureInfo.InvariantCulture) };
} }
catch (System.Exception e) { catch (System.Exception e) {
Debug.LogErrorFormat("Failed to read version info at skeleton '{0}'. It is likely not a valid Spine SkeletonData file.\n{1}", asset.name, e); problemDescription = string.Format("Failed to read version info at skeleton '{0}'. It is likely not a valid Spine SkeletonData file.\n{1}", asset.name, e);
isSpineSkeletonData = false;
return null; return null;
} }
isSpineSkeletonData = true;
return fileVersion; return fileVersion;
} }
public static bool IsJsonFile (TextAsset file) {
string fileText = file.text;
const int maxCharsToCheck = 256;
int numCharsToCheck = Math.Min(fileText.Length, maxCharsToCheck);
if (fileText.IndexOf("\"skeleton\"", 0, numCharsToCheck) != -1 ||
fileText.IndexOf("\"hash\"", 0, numCharsToCheck) != -1 ||
fileText.IndexOf("\"spine\"", 0, numCharsToCheck) != -1)
return true;
int jsonCharCount = 0;
const string jsonChars = "{}:\",";
for (int i = 0; i < numCharsToCheck; ++i) {
char c = fileText[i];
if (jsonChars.IndexOf(c) != -1 || char.IsWhiteSpace(c))
++jsonCharCount;
}
if (jsonCharCount > numCharsToCheck / 10)
return true;
return false;
}
public static CompatibilityProblemInfo GetCompatibilityProblemInfo (VersionInfo fileVersion) { public static CompatibilityProblemInfo GetCompatibilityProblemInfo (VersionInfo fileVersion) {
if (fileVersion == null) if (fileVersion == null) {
return null; return null; // it's most likely not a Spine skeleton file, e.g. another json file. don't report problems.
}
CompatibilityProblemInfo info = new CompatibilityProblemInfo(); CompatibilityProblemInfo info = new CompatibilityProblemInfo();
info.actualVersion = fileVersion; info.actualVersion = fileVersion;