Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Error in UdonSharpPrefabDAG constructor breaks prefabs #107

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,11 @@ private static void UpgradeAssetsIfNeeded()
{
UdonSharpEditorUtility.UpgradePrefabs(GetAllPrefabsWithUdonSharpBehaviours());
}
catch (Exception e)
{
UdonSharpUtils.LogError($"Exception while upgrading prefabs. Exception: {e}");
EditorUtility.DisplayDialog("Error", "Exception while upgrading prefabs! This might be a bug!", "OK");
}
finally
{
UdonSharpEditorCache.Instance.ClearUpgradePassQueue();
Expand Down
183 changes: 88 additions & 95 deletions Packages/com.vrchat.UdonSharp/Editor/UdonSharpPrefabDAG.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,142 +29,135 @@ private class Vertex

public UdonSharpPrefabDAG(IEnumerable<GameObject> allPrefabRoots)
{
try
foreach (GameObject prefabRoot in allPrefabRoots)
{
foreach (GameObject prefabRoot in allPrefabRoots)
Vertex vert = new Vertex() { Prefab = prefabRoot };
_vertices.Add(vert);
_vertexLookup.Add(prefabRoot, vert);
}

foreach (Vertex vertex in _vertices)
{
if (PrefabUtility.IsPartOfVariantPrefab(vertex.Prefab))
{
Vertex vert = new Vertex() { Prefab = prefabRoot };
_vertices.Add(vert);
_vertexLookup.Add(prefabRoot, vert);
Vertex parent = _vertexLookup[PrefabUtility.GetCorrespondingObjectFromSource(vertex.Prefab)];

if (parent == vertex)
{
throw new Exception($"Parent of vertex cannot be the same as the vertex '{vertex.Prefab}'");
}

vertex.Parents.Add(parent);
parent.Children.Add(vertex);
}

foreach (Vertex vertex in _vertices)

foreach (GameObject child in vertex.Prefab.GetComponentsInChildren<Transform>(true)
.Select(e => e.gameObject))
{
if (PrefabUtility.IsPartOfVariantPrefab(vertex.Prefab))
if (child == vertex.Prefab)
{
continue;
}

if (PrefabUtility.IsAnyPrefabInstanceRoot(child))
{
Vertex parent = _vertexLookup[PrefabUtility.GetCorrespondingObjectFromSource(vertex.Prefab)];
GameObject parentPrefab = PrefabUtility.GetCorrespondingObjectFromSource(child);

if (parent == vertex)
if (parentPrefab == null)
{
throw new Exception($"Parent of vertex cannot be the same as the vertex '{vertex.Prefab}'");
throw new Exception($"ParentPrefab of '{child}' is null");
}

vertex.Parents.Add(parent);
parent.Children.Add(vertex);
}

foreach (GameObject child in vertex.Prefab.GetComponentsInChildren<Transform>(true).Select(e => e.gameObject))
{
if (child == vertex.Prefab)
parentPrefab = parentPrefab.transform.root.gameObject;

if (parentPrefab == child)
{
continue;
throw new Exception($"ParentPrefab cannot be the same as child '{child}'");
}

if (PrefabUtility.IsAnyPrefabInstanceRoot(child))
// If a nested prefab is referenced that does *not* have any UdonBehaviours on it, it will not be in the vertex list, and does not need to be linked.
if (_vertexLookup.TryGetValue(parentPrefab, out Vertex parent))
{
GameObject parentPrefab = PrefabUtility.GetCorrespondingObjectFromSource(child);

if (parentPrefab == null)
{
throw new Exception($"ParentPrefab of '{child}' is null");
}

parentPrefab = parentPrefab.transform.root.gameObject;

if (parentPrefab == child)
{
throw new Exception($"ParentPrefab cannot be the same as child '{child}'");
}

// If a nested prefab is referenced that does *not* have any UdonBehaviours on it, it will not be in the vertex list, and does not need to be linked.
if (_vertexLookup.TryGetValue(parentPrefab, out Vertex parent))
{
vertex.Parents.Add(parent);
parent.Children.Add(vertex);
}
vertex.Parents.Add(parent);
parent.Children.Add(vertex);
}
}
}

// Do sorting
HashSet<Vertex> visitedVertices = new HashSet<Vertex>();
}

// Do sorting
HashSet<Vertex> visitedVertices = new HashSet<Vertex>();

// Orphaned nodes with no parents or children go first
foreach (Vertex vertex in _vertices)
// Orphaned nodes with no parents or children go first
foreach (Vertex vertex in _vertices)
{
if (vertex.Children.Count == 0 && vertex.Parents.Count == 0)
{
if (vertex.Children.Count == 0 && vertex.Parents.Count == 0)
{
visitedVertices.Add(vertex);
_sortedVertices.Add(vertex.Prefab);
}
visitedVertices.Add(vertex);
_sortedVertices.Add(vertex.Prefab);
}
}

Queue<Vertex> openSet = new Queue<Vertex>();
Queue<Vertex> openSet = new Queue<Vertex>();

// Find root nodes with no parents
foreach (Vertex vertex in _vertices)
// Find root nodes with no parents
foreach (Vertex vertex in _vertices)
{
if (!visitedVertices.Contains(vertex) && vertex.Parents.Count == 0)
{
if (!visitedVertices.Contains(vertex) && vertex.Parents.Count == 0)
{
openSet.Enqueue(vertex);
}
openSet.Enqueue(vertex);
}
}

while (openSet.Count > 0)
{
Vertex vertex = openSet.Dequeue();

while (openSet.Count > 0)
if (visitedVertices.Contains(vertex))
{
Vertex vertex = openSet.Dequeue();
continue;
}

if (visitedVertices.Contains(vertex))
{
continue;
}
if (vertex.Parents.Count > 0)
{
bool neededParentVisit = false;

if (vertex.Parents.Count > 0)
foreach (Vertex vertexParent in vertex.Parents)
{
bool neededParentVisit = false;

foreach (Vertex vertexParent in vertex.Parents)
{
if (!visitedVertices.Contains(vertexParent))
{
neededParentVisit = true;
openSet.Enqueue(vertexParent);
}
}

if (neededParentVisit)
if (!visitedVertices.Contains(vertexParent))
{
// Re-queue to visit after we have traversed the node's parents
openSet.Enqueue(vertex);
continue;
neededParentVisit = true;
openSet.Enqueue(vertexParent);
}
}

visitedVertices.Add(vertex);
_sortedVertices.Add(vertex.Prefab);

foreach (Vertex vertexChild in vertex.Children)
if (neededParentVisit)
{
openSet.Enqueue(vertexChild);
// Re-queue to visit after we have traversed the node's parents
openSet.Enqueue(vertex);
continue;
}
}

// Sanity check
foreach (Vertex vertex in _vertices)
visitedVertices.Add(vertex);
_sortedVertices.Add(vertex.Prefab);

foreach (Vertex vertexChild in vertex.Children)
{
if (!visitedVertices.Contains(vertex))
{
throw new Exception($"Invalid DAG state: node '{vertex.Prefab}' was not visited.");
}
openSet.Enqueue(vertexChild);
}

_sortedPaths = _sortedVertices.Select(AssetDatabase.GetAssetPath).ToList();
}
catch (Exception e)

// Sanity check
foreach (Vertex vertex in _vertices)
{
UdonSharpUtils.LogError($"Exception while sorting prefabs for upgrade. Falling back to non-sorted set, nested prefabs may not upgrade properly. Exception: {e}");
_sortedPaths = allPrefabRoots.Select(AssetDatabase.GetAssetPath).ToList();
if (!visitedVertices.Contains(vertex))
{
throw new Exception($"Invalid DAG state: node '{vertex.Prefab}' was not visited.");
}
}

_sortedPaths = _sortedVertices.Select(AssetDatabase.GetAssetPath).ToList();
}

/// <summary>
Expand Down