public string BundlizeAssets(string nodeName, string groupkey, List <Asset> sources, string recommendedBundleOutputDir, bool isRun) { var invalids = new List <string>(); foreach (var source in sources) { if (string.IsNullOrEmpty(source.importFrom)) { invalids.Add(source.absoluteAssetPath); } } if (invalids.Any()) { throw new AssetBundleGraphBuildException(nodeName + ": Invalid files to bundle. Following files need to be imported before bundlize: " + string.Join(", ", invalids.ToArray())); } var bundleName = bundleNameTemplate; /* * if contains KEYWORD_WILDCARD, use group identifier to bundlize name. */ if (bundleNameTemplate.Contains(AssetBundleGraphSettings.KEYWORD_WILDCARD)) { var templateHead = bundleNameTemplate.Split(AssetBundleGraphSettings.KEYWORD_WILDCARD)[0]; var templateTail = bundleNameTemplate.Split(AssetBundleGraphSettings.KEYWORD_WILDCARD)[1]; bundleName = (templateHead + groupkey + templateTail + "." + SystemDataUtility.GetCurrentPlatformShortName()).ToLower(); } var bundlePath = FileUtility.PathCombine(recommendedBundleOutputDir, bundleName); for (var i = 0; i < sources.Count; i++) { var source = sources[i]; // if already bundled in this running, avoid changing that name. if (source.isBundled) { continue; } if (isRun) { if (FileUtility.IsMetaFile(source.importFrom)) { continue; } var assetImporter = AssetImporter.GetAtPath(source.importFrom); if (assetImporter == null) { continue; } assetImporter.assetBundleName = bundleName; } // set as this resource is already bundled. sources[i] = Asset.DuplicateAssetWithNewStatus(sources[i], sources[i].isNew, true); } return(bundlePath); }
/** * Perform Run or Setup from parent of given terminal node recursively. */ private static void DoNodeOperation( string nodeId, List <NodeData> allNodes, List <ConnectionData> allConnections, Dictionary <string, Dictionary <string, List <Asset> > > resultDict, Dictionary <string, List <string> > cachedDict, List <string> usedConnectionIds, bool isActualRun, Action <string, float> updateHandler = null ) { var relatedNodes = allNodes.Where(relation => relation.nodeId == nodeId).ToList(); if (!relatedNodes.Any()) { return; } var currentNodeData = relatedNodes[0]; if (currentNodeData.IsAlreadyDone()) { return; } var nodeName = currentNodeData.nodeName; var nodeKind = currentNodeData.nodeKind; /* * Perform prarent node recursively from this node */ foreach (var connectionToParent in currentNodeData.connectionToParents) { var parentNodeId = connectionToParent.fromNodeId; var usedConnectionId = connectionToParent.connectionId; if (usedConnectionIds.Contains(usedConnectionId)) { throw new NodeException("connection loop detected.", parentNodeId); } usedConnectionIds.Add(usedConnectionId); var parentNode = allNodes.Where(node => node.nodeId == parentNodeId).ToList(); if (!parentNode.Any()) { return; } var parentNodeKind = parentNode[0].nodeKind; // check node kind order. SystemDataValidator.ValidateAssertNodeOrder(parentNodeKind, nodeKind); DoNodeOperation(parentNodeId, allNodes, allConnections, resultDict, cachedDict, usedConnectionIds, isActualRun, updateHandler); } /* * Perform node operation for this node */ // connections Ids from this node to child nodes. non-ordered. // actual running order depends on order of Node's OutputPoint order. var nonOrderedConnectionsFromThisNodeToChildNode = allConnections .Where(con => con.fromNodeId == nodeId) .ToList(); var orderedNodeOutputPointIds = allNodes.Where(node => node.nodeId == nodeId).SelectMany(node => node.outputPointIds).ToList(); /* * get connection ids which is orderd by node's outputPoint-order. */ var orderedConnectionIds = new List <string>(nonOrderedConnectionsFromThisNodeToChildNode.Count); foreach (var orderedNodeOutputPointId in orderedNodeOutputPointIds) { foreach (var nonOrderedConnectionFromThisNodeToChildNode in nonOrderedConnectionsFromThisNodeToChildNode) { var nonOrderedConnectionOutputPointId = nonOrderedConnectionFromThisNodeToChildNode.fromNodeOutputPointId; if (orderedNodeOutputPointId == nonOrderedConnectionOutputPointId) { orderedConnectionIds.Add(nonOrderedConnectionFromThisNodeToChildNode.connectionId); continue; } } } /* * FilterNode and BundlizerNode uses specific multiple output connections. * ExportNode does not have output. * but all other nodes has only one output connection and uses first connection. */ var firstConnectionIdFromThisNodeToChildNode = string.Empty; if (orderedConnectionIds.Any()) { firstConnectionIdFromThisNodeToChildNode = orderedConnectionIds[0]; } if (updateHandler != null) { updateHandler(nodeId, 0f); } /* * has next node, run first time. */ var alreadyCachedPaths = new List <string>(); if (cachedDict.ContainsKey(nodeId)) { alreadyCachedPaths.AddRange(cachedDict[nodeId]); } /* * load already exist cache from node. */ alreadyCachedPaths.AddRange(GetCachedDataByNodeKind(nodeKind, nodeId)); var inputParentResults = new Dictionary <string, List <Asset> >(); var receivingConnectionIds = allConnections .Where(con => con.toNodeId == nodeId) .Select(con => con.connectionId) .ToList(); foreach (var connecionId in receivingConnectionIds) { if (!resultDict.ContainsKey(connecionId)) { continue; } var result = resultDict[connecionId]; foreach (var groupKey in result.Keys) { if (!inputParentResults.ContainsKey(groupKey)) { inputParentResults[groupKey] = new List <Asset>(); } inputParentResults[groupKey].AddRange(result[groupKey]); } } /* * the Action passes to NodeOperaitons. * It stores result to resultDict. */ Action <string, string, Dictionary <string, List <Asset> >, List <string> > Output = (string dataSourceNodeId, string targetConnectionId, Dictionary <string, List <Asset> > result, List <string> justCached) => { var targetConnectionIds = allConnections .Where(con => con.connectionId == targetConnectionId) .Select(con => con.connectionId) .ToList(); if (!targetConnectionIds.Any()) { // if next connection does not exist, no results for next. // save results to resultDict with this endpoint node's id. resultDict[dataSourceNodeId] = new Dictionary <string, List <Asset> >(); foreach (var groupKey in result.Keys) { if (!resultDict[dataSourceNodeId].ContainsKey(groupKey)) { resultDict[dataSourceNodeId][groupKey] = new List <Asset>(); } resultDict[dataSourceNodeId][groupKey].AddRange(result[groupKey]); } return; } if (!resultDict.ContainsKey(targetConnectionId)) { resultDict[targetConnectionId] = new Dictionary <string, List <Asset> >(); } /* * merge connection result by group key. */ foreach (var groupKey in result.Keys) { if (!resultDict[targetConnectionId].ContainsKey(groupKey)) { resultDict[targetConnectionId][groupKey] = new List <Asset>(); } resultDict[targetConnectionId][groupKey].AddRange(result[groupKey]); } if (isActualRun) { if (!cachedDict.ContainsKey(nodeId)) { cachedDict[nodeId] = new List <string>(); } cachedDict[nodeId].AddRange(justCached); } }; try { if (isActualRun) { switch (nodeKind) { /* * Scripts */ case AssetBundleGraphSettings.NodeKind.FILTER_SCRIPT: { var scriptClassName = currentNodeData.scriptClassName; var executor = SystemDataUtility.CreateNodeOperationInstance <FilterBase>(scriptClassName, nodeId); executor.Run(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.PREFABRICATOR_SCRIPT: { var scriptClassName = currentNodeData.scriptClassName; var executor = SystemDataUtility.CreateNodeOperationInstance <PrefabricatorBase>(scriptClassName, nodeId); executor.Run(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } /* * GUIs */ case AssetBundleGraphSettings.NodeKind.LOADER_GUI: { var currentLoadFilePath = SystemDataUtility.GetCurrentPlatformValue(currentNodeData.loadFilePath); var executor = new IntegratedGUILoader(FileUtility.GetPathWithAssetsPath(currentLoadFilePath)); executor.Run(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.FILTER_GUI: { /* * Filter requires "outputPoint ordered exist connection Id and Fake connection Id" for * exhausting assets by keyword and type correctly. * * outputPoint which has connection can through assets by keyword and keytype, * also outputPoint which doesn't have connection should take assets by keyword and keytype. */ var orderedConnectionIdsAndFakeConnectionIds = new string[orderedNodeOutputPointIds.Count]; for (var i = 0; i < orderedNodeOutputPointIds.Count; i++) { var orderedNodeOutputPointId = orderedNodeOutputPointIds[i]; foreach (var nonOrderedConnectionFromThisNodeToChildNode in nonOrderedConnectionsFromThisNodeToChildNode) { var connectionOutputPointId = nonOrderedConnectionFromThisNodeToChildNode.fromNodeOutputPointId; if (orderedNodeOutputPointId == connectionOutputPointId) { orderedConnectionIdsAndFakeConnectionIds[i] = nonOrderedConnectionFromThisNodeToChildNode.connectionId; break; } else { orderedConnectionIdsAndFakeConnectionIds[i] = AssetBundleGraphSettings.FILTER_FAKE_CONNECTION_ID; } } } var executor = new IntegratedGUIFilter(orderedConnectionIdsAndFakeConnectionIds, currentNodeData.containsKeywords, currentNodeData.containsKeytypes); executor.Run(nodeName, nodeId, string.Empty, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.IMPORTSETTING_GUI: { var executor = new IntegratedGUIImportSetting(); executor.Run(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.MODIFIER_GUI: { var specificScriptClass = currentNodeData.scriptClassName; var executor = new IntegratedGUIModifier(specificScriptClass, SystemDataUtility.GetCurrentPlatformShortName()); executor.Run(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.GROUPING_GUI: { var executor = new IntegratedGUIGrouping(SystemDataUtility.GetCurrentPlatformValue(currentNodeData.groupingKeyword)); executor.Run(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.PREFABRICATOR_GUI: { var scriptClassName = currentNodeData.scriptClassName; if (string.IsNullOrEmpty(scriptClassName)) { Debug.LogError(nodeName + ": Classname is empty. Set valid classname. Configure valid script name from editor."); break; } var executor = SystemDataUtility.CreateNodeOperationInstance <PrefabricatorBase>(scriptClassName, nodeId); executor.Run(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.BUNDLIZER_GUI: { /* * Bundlizer requires assetOutputConnectionId and additional resourceOutputConnectionId. * both-connected, or both-not-connected, or one of them is connected. 4 patterns exists. * * Bundler Node's outputPoint [0] is always the point for assetOutputConnectionId. * Bundler Node's outputPoint [1] is always the point for resourceOutputConnectionId. * * if one of these outputPoint don't have connection, use Fake connection id for correct output. * * * unorderedConnectionId \ * ----> orderedConnectionIdsAndFakeConnectionIds. * orderedOutputPointId / */ var orderedConnectionIdsAndFakeConnectionIds = new string[orderedNodeOutputPointIds.Count]; for (var i = 0; i < orderedNodeOutputPointIds.Count; i++) { var orderedNodeOutputPointId = orderedNodeOutputPointIds[i]; foreach (var nonOrderedConnectionFromThisNodeToChildNode in nonOrderedConnectionsFromThisNodeToChildNode) { var connectionOutputPointId = nonOrderedConnectionFromThisNodeToChildNode.fromNodeOutputPointId; if (orderedNodeOutputPointId == connectionOutputPointId) { orderedConnectionIdsAndFakeConnectionIds[i] = nonOrderedConnectionFromThisNodeToChildNode.connectionId; break; } else { orderedConnectionIdsAndFakeConnectionIds[i] = AssetBundleGraphSettings.BUNDLIZER_FAKE_CONNECTION_ID; } } } var bundleNameTemplate = SystemDataUtility.GetCurrentPlatformValue(currentNodeData.bundleNameTemplate); /* * TODO: variants needs to be execute Setup/Run as many times as its variants */ var executor = new IntegratedGUIBundlizer(bundleNameTemplate, orderedConnectionIdsAndFakeConnectionIds[0], currentNodeData.variants); executor.Run(nodeName, nodeId, string.Empty, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.BUNDLEBUILDER_GUI: { var bundleOptions = SystemDataUtility.GetCurrentPlatformValue(currentNodeData.enabledBundleOptions); var executor = new IntegratedGUIBundleBuilder(bundleOptions, allNodes.Select(nodeData => nodeData.nodeId).ToList()); executor.Run(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.EXPORTER_GUI: { var exportTo = SystemDataUtility.GetCurrentPlatformValue(currentNodeData.exportFilePath); var executor = new IntegratedGUIExporter(FileUtility.GetPathWithProjectPath(exportTo)); executor.Run(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } default: { Debug.LogError(nodeName + " is defined as unknown kind of node. value:" + nodeKind); break; } } } else { switch (nodeKind) { /* * Scripts */ case AssetBundleGraphSettings.NodeKind.FILTER_SCRIPT: { var scriptClassName = currentNodeData.scriptClassName; var executor = SystemDataUtility.CreateNodeOperationInstance <FilterBase>(scriptClassName, nodeId); executor.Setup(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.PREFABRICATOR_SCRIPT: { var scriptClassName = currentNodeData.scriptClassName; var executor = SystemDataUtility.CreateNodeOperationInstance <PrefabricatorBase>(scriptClassName, nodeId); executor.Setup(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } /* * GUIs */ case AssetBundleGraphSettings.NodeKind.LOADER_GUI: { var currentLoadFilePath = SystemDataUtility.GetCurrentPlatformValue(currentNodeData.loadFilePath); var executor = new IntegratedGUILoader(FileUtility.GetPathWithAssetsPath(currentLoadFilePath)); executor.Setup(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.FILTER_GUI: { /* * Filter requires "outputPoint ordered exist connection Id and Fake connection Id" for * exhausting assets by keyword and type correctly. * * outputPoint which has connection can through assets by keyword and keytype, * also outputPoint which doesn't have connection should take assets by keyword and keytype. */ var orderedConnectionIdsAndFakeConnectionIds = new string[orderedNodeOutputPointIds.Count]; for (var i = 0; i < orderedNodeOutputPointIds.Count; i++) { var orderedNodeOutputPointId = orderedNodeOutputPointIds[i]; foreach (var nonOrderedConnectionFromThisNodeToChildNode in nonOrderedConnectionsFromThisNodeToChildNode) { var connectionOutputPointId = nonOrderedConnectionFromThisNodeToChildNode.fromNodeOutputPointId; if (orderedNodeOutputPointId == connectionOutputPointId) { orderedConnectionIdsAndFakeConnectionIds[i] = nonOrderedConnectionFromThisNodeToChildNode.connectionId; break; } else { orderedConnectionIdsAndFakeConnectionIds[i] = AssetBundleGraphSettings.FILTER_FAKE_CONNECTION_ID; } } } var executor = new IntegratedGUIFilter(orderedConnectionIdsAndFakeConnectionIds, currentNodeData.containsKeywords, currentNodeData.containsKeytypes); executor.Setup(nodeName, nodeId, string.Empty, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.IMPORTSETTING_GUI: { var executor = new IntegratedGUIImportSetting(); executor.Setup(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.MODIFIER_GUI: { var specificScriptClass = currentNodeData.scriptClassName; var executor = new IntegratedGUIModifier(specificScriptClass, SystemDataUtility.GetCurrentPlatformShortName()); executor.Setup(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.GROUPING_GUI: { var executor = new IntegratedGUIGrouping(SystemDataUtility.GetCurrentPlatformValue(currentNodeData.groupingKeyword)); executor.Setup(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.PREFABRICATOR_GUI: { var scriptClassName = currentNodeData.scriptClassName; if (string.IsNullOrEmpty(scriptClassName)) { AssetBundleGraphEditorWindow.AddNodeException(new NodeException(nodeName + ": Classname is empty. Set valid classname.", nodeId)); break; } try { var executor = SystemDataUtility.CreateNodeOperationInstance <PrefabricatorBase>(scriptClassName, nodeId); executor.Setup(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); } catch (NodeException e) { AssetBundleGraphEditorWindow.AddNodeException(e); break; } break; } case AssetBundleGraphSettings.NodeKind.BUNDLIZER_GUI: { /* * Bundlizer requires assetOutputConnectionId and additional resourceOutputConnectionId. * both-connected, or both-not-connected, or one of them is connected. 4 patterns exists. * * Bundler Node's outputPoint [0] is always the point for assetOutputConnectionId. * Bundler Node's outputPoint [1] is always the point for resourceOutputConnectionId. * * if one of these outputPoint don't have connection, use Fake connection id for correct output. * * * unorderedConnectionId \ * ----> orderedConnectionIdsAndFakeConnectionIds. * orderedOutputPointId / */ var orderedConnectionIdsAndFakeConnectionIds = new string[orderedNodeOutputPointIds.Count]; for (var i = 0; i < orderedNodeOutputPointIds.Count; i++) { var orderedNodeOutputPointId = orderedNodeOutputPointIds[i]; foreach (var nonOrderedConnectionFromThisNodeToChildNode in nonOrderedConnectionsFromThisNodeToChildNode) { var connectionOutputPointId = nonOrderedConnectionFromThisNodeToChildNode.fromNodeOutputPointId; if (orderedNodeOutputPointId == connectionOutputPointId) { orderedConnectionIdsAndFakeConnectionIds[i] = nonOrderedConnectionFromThisNodeToChildNode.connectionId; break; } else { orderedConnectionIdsAndFakeConnectionIds[i] = AssetBundleGraphSettings.BUNDLIZER_FAKE_CONNECTION_ID; } } } var bundleNameTemplate = SystemDataUtility.GetCurrentPlatformValue(currentNodeData.bundleNameTemplate); /* * TODO: variants needs to be execute Setup/Run as many times as its variants */ var executor = new IntegratedGUIBundlizer(bundleNameTemplate, orderedConnectionIdsAndFakeConnectionIds[0], currentNodeData.variants); executor.Setup(nodeName, nodeId, string.Empty, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.BUNDLEBUILDER_GUI: { var bundleOptions = SystemDataUtility.GetCurrentPlatformValue(currentNodeData.enabledBundleOptions); var executor = new IntegratedGUIBundleBuilder(bundleOptions, allNodes.Select(nodeData => nodeData.nodeId).ToList()); executor.Setup(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } case AssetBundleGraphSettings.NodeKind.EXPORTER_GUI: { var exportTo = SystemDataUtility.GetCurrentPlatformValue(currentNodeData.exportFilePath); var executor = new IntegratedGUIExporter(FileUtility.GetPathWithProjectPath(exportTo)); executor.Setup(nodeName, nodeId, firstConnectionIdFromThisNodeToChildNode, inputParentResults, alreadyCachedPaths, Output); break; } default: { Debug.LogError(nodeName + " is defined as unknown kind of node. value:" + nodeKind); break; } } } } catch (NodeException e) { AssetBundleGraphEditorWindow.AddNodeException(e); //Debug.LogError("error occured:\"" + e.reason + "\", please check information on node."); return; //throw new AssetBundleGraphException(nodeName + ": " + e.reason); } currentNodeData.Done(); if (updateHandler != null) { updateHandler(nodeId, 1f); } }