/// <inheritdoc /> public Hash128 GetHash128(IBuildLogger log) { #if UNITY_2019_3_OR_NEWER CacheEntry entry = BuildCacheUtility.GetCacheEntry(Scene); #else CacheEntry entry = BuildCacheUtility.GetCacheEntry(ProcessedScene); #endif HashSet <CacheEntry> hashObjects = new HashSet <CacheEntry>(); using (log.ScopedStep(LogLevel.Verbose, $"Gather Objects", Command.fileName)) Command.GatherSerializedObjectCacheEntries(hashObjects); List <Hash128> hashes = new List <Hash128>(); using (log.ScopedStep(LogLevel.Verbose, $"Hashing Command", Command.fileName)) hashes.Add(Command.GetHash128()); using (log.ScopedStep(LogLevel.Verbose, $"Hashing UsageSet", Command.fileName)) hashes.Add(UsageSet.GetHash128()); using (log.ScopedStep(LogLevel.Verbose, $"Hashing ReferenceMap", Command.fileName)) hashes.Add(ReferenceMap.GetHash128()); using (log.ScopedStep(LogLevel.Verbose, $"Hashing Objects", Command.fileName)) hashes.Add(HashingMethods.Calculate(hashObjects).ToHash128()); hashes.Add(new Hash128(0, 0, 0, (uint)QualitySettingsApi.GetNumberOfLODsStripped())); hashes.Add(DependencyHash); return(HashingMethods.Calculate(hashes, Scene, entry).ToHash128()); }
/// <inheritdoc /> public Hash128 GetHash128(IBuildLogger log) { HashSet <CacheEntry> hashObjects = new HashSet <CacheEntry>(); using (log.ScopedStep(LogLevel.Verbose, $"Gather Objects {GetType().Name}", Command.fileName)) { if (Command.serializeObjects != null) { foreach (var serializeObject in Command.serializeObjects) { hashObjects.Add(BuildCacheUtility.GetCacheEntry(serializeObject.serializationObject)); } } } List <Hash128> hashes = new List <Hash128>(); using (log.ScopedStep(LogLevel.Verbose, $"Hashing Command", Command.fileName)) hashes.Add(Command.GetHash128()); using (log.ScopedStep(LogLevel.Verbose, $"Hashing UsageSet", Command.fileName)) hashes.Add(UsageSet.GetHash128()); using (log.ScopedStep(LogLevel.Verbose, $"Hashing ReferenceMap", Command.fileName)) hashes.Add(ReferenceMap.GetHash128()); using (log.ScopedStep(LogLevel.Verbose, $"Hashing Objects", Command.fileName)) hashes.Add(HashingMethods.Calculate(hashObjects).ToHash128()); hashes.Add(DependencyHash); return(HashingMethods.Calculate(hashes).ToHash128()); }
CacheEntry GetCacheEntry(IWriteOperation operation, BuildSettings settings, BuildUsageTagGlobal globalUsage, bool onlySaveFirstSerializedObject) { using (m_Log.ScopedStep(LogLevel.Verbose, "GetCacheEntry", operation.Command.fileName)) { var entry = new CacheEntry(); entry.Type = CacheEntry.EntryType.Data; entry.Guid = HashingMethods.Calculate("WriteSerializedFiles", operation.Command.fileName).ToGUID(); entry.Hash = HashingMethods.Calculate(Version, operation.GetHash128(m_Log), settings.GetHash128(), globalUsage, onlySaveFirstSerializedObject, GetPlayerSettingsHash128(settings.target)).ToHash128(); entry.Version = Version; return(entry); } }
static void CopyFileWithTimestampIfDifferent(string srcPath, string destPath, IBuildLogger log) { if (srcPath == destPath) { return; } DateTime time = File.GetLastWriteTime(srcPath); DateTime destTime = File.Exists(destPath) ? File.GetLastWriteTime(destPath) : new DateTime(); if (destTime == time) { return; } using (log.ScopedStep(LogLevel.Verbose, $"Copying File {srcPath} -> {destPath}")) { var directory = Path.GetDirectoryName(destPath); if (!string.IsNullOrEmpty(directory)) { Directory.CreateDirectory(directory); } File.Copy(srcPath, destPath, true); } }
/// <inheritdoc /> public ReturnCode Run() { m_CustomAssets = new CustomAssets(); m_GlobalUsage = m_DependencyData.GlobalUsage; foreach (SceneDependencyInfo sceneInfo in m_DependencyData.SceneInfo.Values) { m_GlobalUsage |= sceneInfo.globalUsage; } foreach (CustomContent info in m_Content.CustomAssets) { if (!m_Tracker.UpdateInfoUnchecked(info.Asset.ToString())) { return(ReturnCode.Canceled); } using (m_Log.ScopedStep(LogLevel.Verbose, "CustomAssetDependency", info.Asset.ToString())) info.Processor(info.Asset, this); } // Add all the additional global usage for custom assets back into the dependency data result // for use in the write serialized file build task m_DependencyData.GlobalUsage |= m_CustomUsage; return(ReturnCode.Success); }
static void AddLocalFilesToTreeIfNotEnumerated(AddressableAssetTree tree, string path, IBuildLogger logger) { AddressableAssetTree.TreeNode pathNode = tree.FindNode(path, true); if (pathNode == null || pathNode.HasEnumerated) // Already enumerated { return; } pathNode.HasEnumerated = true; using (logger.ScopedStep(LogLevel.Info, $"Enumerating {path}")) { foreach (string filename in Directory.EnumerateFileSystemEntries(path, "*.*", SearchOption.AllDirectories)) { if (!AddressableAssetUtility.IsPathValidForEntry(filename)) { continue; } string convertedPath = filename.Replace('\\', '/'); var node = tree.FindNode(convertedPath, true); node.IsFolder = AssetDatabase.IsValidFolder(filename); node.HasEnumerated = true; } } }
private static void ProcessCatalogEntriesForBuild(AddressableAssetsBuildContext aaContext, IBuildLogger log, IEnumerable <AddressableAssetGroup> validGroups, AddressablesDataBuilderInput builderInput, IBundleWriteData writeData, List <CachedAssetState> carryOverCachedState, Dictionary <string, string> bundleToInternalId) { using (log.ScopedStep(LogLevel.Info, "Catalog Entries.")) using (var progressTracker = new UnityEditor.Build.Pipeline.Utilities.ProgressTracker()) { progressTracker.UpdateTask("Post Processing Catalog Entries"); Dictionary <string, ContentCatalogDataEntry> locationIdToCatalogEntryMap = BuildLocationIdToCatalogEntryMap(aaContext.locations); if (builderInput.PreviousContentState != null) { ContentUpdateContext contentUpdateContext = new ContentUpdateContext() { BundleToInternalBundleIdMap = bundleToInternalId, GuidToPreviousAssetStateMap = BuildGuidToCachedAssetStateMap(builderInput.PreviousContentState, aaContext.settings), IdToCatalogDataEntryMap = locationIdToCatalogEntryMap, WriteData = writeData, ContentState = builderInput.PreviousContentState, Registry = builderInput.Registry, PreviousAssetStateCarryOver = carryOverCachedState }; RevertUnchangedAssetsToPreviousAssetState.Run(aaContext, contentUpdateContext); } else { foreach (var assetGroup in validGroups) { SetAssetEntriesBundleFileIdToCatalogEntryBundleFileId(assetGroup.entries, bundleToInternalId, writeData, locationIdToCatalogEntryMap); } } } bundleToInternalId.Clear(); }
public static List <string> EnumerateAddressableFolder(string path, AddressableAssetSettings settings, bool recurseAll, IBuildLogger logger = null) { if (!AssetDatabase.IsValidFolder(path)) { throw new Exception($"Path {path} cannot be enumerated because it does not exist"); } AddressableAssetTree tree = m_PrecomputedTree != null ? m_PrecomputedTree : BuildAddressableTree(settings, logger); if (tree == null) { return(new List <string>()); } AddLocalFilesToTreeIfNotEnumerated(tree, path, logger); List <string> files = new List <string>(); using (logger.ScopedStep(LogLevel.Info, $"Enumerating Addressables Tree {path}")) { foreach (string file in tree.Enumerate(path, recurseAll)) { if (BuiltinSceneCache.Contains(file)) { continue; } files.Add(file); } } return(files); }
/// <summary> /// Build the specified data with the provided builderInput. This is the public entry point. /// Child class overrides should use <see cref="BuildDataImplementation{TResult}"/> /// </summary> /// <typeparam name="TResult">The type of data to build.</typeparam> /// <param name="builderInput">The builderInput object used in the build.</param> /// <returns>The build data result.</returns> public TResult BuildData <TResult>(AddressablesDataBuilderInput builderInput) where TResult : IDataBuilderResult { if (!CanBuildData <TResult>()) { var message = "Data builder " + Name + " cannot build requested type: " + typeof(TResult); Debug.LogError(message); return(AddressableAssetBuildResult.CreateResult <TResult>(null, 0, message)); } m_Log = (builderInput.Logger != null) ? builderInput.Logger : new BuildLog(); AddressablesRuntimeProperties.ClearCachedPropertyValues(); TResult result; // Append the file registry to the results using (m_Log.ScopedStep(LogLevel.Info, $"Building {this.Name}")) { result = BuildDataImplementation <TResult>(builderInput); if (result != null) { result.FileRegistry = builderInput.Registry; } } if (builderInput.Logger == null && m_Log != null) { WriteBuildLog((BuildLog)m_Log, Path.GetDirectoryName(Application.dataPath) + "/Library/com.unity.addressables"); } return(result); }
static void BeginPrecomputedEnumerationSession(AddressableAssetSettings settings, bool prepopulateAssetsFolder, IBuildLogger logger) { using (logger.ScopedStep(LogLevel.Info, "AddressablesFileEnumeration.BeginPrecomputedEnumerationSession")) { m_PrecomputedTree = BuildAddressableTree(settings, logger); if (m_PrecomputedTree != null && prepopulateAssetsFolder) { AddLocalFilesToTreeIfNotEnumerated(m_PrecomputedTree, "Assets", logger); } } }
static void CopyToOutputLocation(string writePath, string finalPath, IBuildLogger log) { if (finalPath != writePath) { using (log.ScopedStep(LogLevel.Verbose, $"Copying From Cache {writePath} -> {finalPath}")) { var directory = Path.GetDirectoryName(finalPath); Directory.CreateDirectory(directory); File.Copy(writePath, finalPath, true); } } }
/// <inheritdoc /> public Hash128 GetHash128(IBuildLogger log) { HashSet <CacheEntry> hashObjects = new HashSet <CacheEntry>(); using (log.ScopedStep(LogLevel.Verbose, $"Gather Objects {GetType().Name}", Command.fileName)) Command.GatherSerializedObjectCacheEntries(hashObjects); List <Hash128> hashes = new List <Hash128>(); using (log.ScopedStep(LogLevel.Verbose, $"Hashing Command", Command.fileName)) hashes.Add(Command.GetHash128()); using (log.ScopedStep(LogLevel.Verbose, $"Hashing UsageSet", Command.fileName)) hashes.Add(UsageSet.GetHash128()); using (log.ScopedStep(LogLevel.Verbose, $"Hashing ReferenceMap", Command.fileName)) hashes.Add(ReferenceMap.GetHash128()); using (log.ScopedStep(LogLevel.Verbose, $"Hashing Objects", Command.fileName)) hashes.Add(HashingMethods.Calculate(hashObjects).ToHash128()); hashes.Add(DependencyHash); return(HashingMethods.Calculate(hashes).ToHash128()); }
static void PostArchiveProcessing(List <ArchiveWorkItem> items, List <List <string> > assetFileList, Dictionary <string, string> filenameToBundleName, IBuildLogger log) { using (log.ScopedStep(LogLevel.Info, "PostArchiveProcessing")) { Dictionary <string, string[]> bundleDependencies = CalculateBundleDependencies(assetFileList, filenameToBundleName); foreach (ArchiveWorkItem item in items) { // apply bundle dependencies item.ResultDetails.Dependencies = bundleDependencies.ContainsKey(item.BundleName) ? bundleDependencies[item.BundleName] : new string[0]; item.ResultDetails.Hash = CalculateHashVersion(item, item.ResultDetails.Dependencies); } } }
/// <summary> /// Build the specified data with the provided builderInput. This is the public entry point. /// Child class overrides should use <see cref="BuildDataImplementation{TResult}"/> /// </summary> /// <typeparam name="TResult">The type of data to build.</typeparam> /// <param name="builderInput">The builderInput object used in the build.</param> /// <returns>The build data result.</returns> public TResult BuildData <TResult>(AddressablesDataBuilderInput builderInput) where TResult : IDataBuilderResult { if (!CanBuildData <TResult>()) { var message = "Data builder " + Name + " cannot build requested type: " + typeof(TResult); Debug.LogError(message); return(AddressableAssetBuildResult.CreateResult <TResult>(null, 0, message)); } m_Log = (builderInput.Logger != null) ? builderInput.Logger : new BuildLog(); AddressablesRuntimeProperties.ClearCachedPropertyValues(); TResult result = default; // Append the file registry to the results using (m_Log.ScopedStep(LogLevel.Info, $"Building {this.Name}")) { try { result = BuildDataImplementation <TResult>(builderInput); } catch (Exception e) { string errMessage; if (e.Message == "path") { errMessage = "Invalid path detected during build. Check for unmatched brackets in your active profile's variables."; } else { errMessage = e.Message; } Debug.LogError(errMessage); return(AddressableAssetBuildResult.CreateResult <TResult>(null, 0, errMessage)); } if (result != null) { result.FileRegistry = builderInput.Registry; } } if (builderInput.Logger == null && m_Log != null) { WriteBuildLog((BuildLog)m_Log, Path.GetDirectoryName(Application.dataPath) + "/" + Addressables.LibraryPath); } return(result); }
static void PostArchiveProcessing(List <ArchiveWorkItem> items, List <List <string> > assetFileList, Dictionary <string, string> filenameToBundleName, IBuildLogger log) { using (log.ScopedStep(LogLevel.Info, "PostArchiveProcessing")) { Dictionary <string, string[]> bundleDependencies = CalculateBundleDependencies(assetFileList, filenameToBundleName); foreach (ArchiveWorkItem item in items) { // apply bundle dependencies item.ResultDetails.Dependencies = bundleDependencies.ContainsKey(item.BundleName) ? bundleDependencies[item.BundleName] : new string[0]; // set the hash on the bundle result. must be applied here because the ToString of the Hash128 can't be called on a thread item.ResultDetails.Hash = item.ResultHash; } } }
internal static AddressableAssetTree BuildAddressableTree(AddressableAssetSettings settings, IBuildLogger logger = null) { using (logger.ScopedStep(LogLevel.Verbose, "BuildAddressableTree")) { if (!ExtractAddressablePaths(settings, out HashSet <string> paths)) { return(null); } AddressableAssetTree tree = new AddressableAssetTree(); foreach (string path in paths) { AddressableAssetTree.TreeNode node = tree.FindNode(path, true); node.IsAddressable = true; } return(tree); } }
void IRunCachedCallbacks <Item> .ProcessUncached(WorkItem <Item> item) { IWriteOperation op = m_WriteData.WriteOperations[item.Index]; string targetDir = m_UseCache != null?m_UseCache.GetCachedArtifactsDirectory(item.entry) : m_Parameters.TempOutputFolder; Directory.CreateDirectory(targetDir); using (m_Log.ScopedStep(LogLevel.Info, $"Writing {op.GetType().Name}", op.Command.fileName)) item.Context.Result = op.Write(targetDir, m_BuildSettings, m_GlobalUsage); item.Context.MetaData = CalculateFileMetadata(ref item.Context.Result); if (ScriptableBuildPipeline.slimWriteResults) { SlimifySerializedObjects(ref item.Context.Result); } }
static private void ArchiveSingleItem(ArchiveWorkItem item, string tempOutputFolder, IBuildLogger log) { using (log.ScopedStep(LogLevel.Info, "ArchiveSingleItem", item.BundleName)) { item.ResultDetails = new BundleDetails(); string writePath = string.Format("{0}/{1}", tempOutputFolder, item.BundleName); if (!string.IsNullOrEmpty(item.CachedArtifactPath)) { writePath = item.CachedArtifactPath; } Directory.CreateDirectory(Path.GetDirectoryName(writePath)); item.ResultDetails.FileName = item.OutputFilePath; item.ResultDetails.Crc = ContentBuildInterface.ArchiveAndCompress(item.ResourceFiles.ToArray(), writePath, item.Compression); CopyFileWithTimestampIfDifferent(writePath, item.ResultDetails.FileName, log); } }
static private void ArchiveSingleItem(ArchiveWorkItem item, Dictionary <string, ulong> fileOffsets, string tempOutputFolder, IBuildLogger log) { using (log.ScopedStep(LogLevel.Info, $"Archive {item.BundleName}")) { item.ResultDetails = new BundleDetails(); string writePath = string.Format("{0}/{1}", tempOutputFolder, item.BundleName); if (!string.IsNullOrEmpty(item.CachedArtifactPath)) { writePath = item.CachedArtifactPath; } Directory.CreateDirectory(Path.GetDirectoryName(writePath)); item.ResultDetails.FileName = item.OutputFilePath; item.ResultDetails.Crc = ContentBuildInterface.ArchiveAndCompress(item.ResourceFiles, writePath, item.Compression); item.ResultHash = CalculateHashVersion(fileOffsets, item.ResourceFiles, item.ResultDetails.Dependencies); CopyToOutputLocation(writePath, item.ResultDetails.FileName, log); } }
static private bool ArchiveItems(List <ArchiveWorkItem> items, Dictionary <string, ulong> fileOffsets, string tempOutputFolder, IProgressTracker tracker, bool threaded, IBuildLogger log) { using (log.ScopedStep(LogLevel.Info, "ArchiveItems", threaded)) { log?.AddEntry(LogLevel.Info, $"Archiving {items.Count} Bundles"); if (threaded) { return(ArchiveItemsThreaded(items, fileOffsets, tempOutputFolder, tracker, log)); } foreach (ArchiveWorkItem item in items) { if (tracker != null && !tracker.UpdateInfoUnchecked(item.BundleName)) { return(false); } ArchiveSingleItem(item, fileOffsets, tempOutputFolder, log); } return(true); } }
#pragma warning restore 649 /// <inheritdoc /> public ReturnCode Run() { Dictionary <string, WriteCommand> fileToCommand; Dictionary <string, HashSet <ObjectIdentifier> > forwardObjectDependencies; Dictionary <string, HashSet <string> > forwardFileDependencies; Dictionary <string, HashSet <GUID> > reverseAssetDependencies; // BuildReferenceMap details what objects exist in other bundles that objects in a source bundle depend upon (forward dependencies) // BuildUsageTagSet details the conditional data needed to be written by objects in a source bundle that is in used by objects in other bundles (reverse dependencies) using (m_Log.ScopedStep(LogLevel.Info, $"Temporary Map Creations")) { fileToCommand = m_WriteData.WriteOperations.ToDictionary(x => x.Command.internalName, x => x.Command); forwardObjectDependencies = new Dictionary <string, HashSet <ObjectIdentifier> >(); forwardFileDependencies = new Dictionary <string, HashSet <string> >(); reverseAssetDependencies = new Dictionary <string, HashSet <GUID> >(); foreach (var pair in m_WriteData.AssetToFiles) { GUID asset = pair.Key; List <string> files = pair.Value; // The includes for an asset live in the first file, references could live in any file forwardObjectDependencies.GetOrAdd(files[0], out HashSet <ObjectIdentifier> objectDependencies); forwardFileDependencies.GetOrAdd(files[0], out HashSet <string> fileDependencies); // Grab the list of object references for the asset or scene and add them to the forward dependencies hash set for this file (write command) if (m_DependencyData.AssetInfo.TryGetValue(asset, out AssetLoadInfo assetInfo)) { objectDependencies.UnionWith(assetInfo.referencedObjects); } if (m_DependencyData.SceneInfo.TryGetValue(asset, out SceneDependencyInfo sceneInfo)) { objectDependencies.UnionWith(sceneInfo.referencedObjects); } // Grab the list of file references for the asset or scene and add them to the forward dependencies hash set for this file (write command) // While doing so, also add the asset to the reverse dependencies hash set for all the other files it depends upon. // We already ensure BuildReferenceMap & BuildUsageTagSet contain the objects in this write command in GenerateBundleCommands. So skip over the first file (self) for (int i = 1; i < files.Count; i++) { fileDependencies.Add(files[i]); reverseAssetDependencies.GetOrAdd(files[i], out HashSet <GUID> reverseDependencies); reverseDependencies.Add(asset); } } } // Using the previously generated forward dependency maps, update the BuildReferenceMap per WriteCommand to contain just the references that we care about using (m_Log.ScopedStep(LogLevel.Info, $"Populate BuildReferenceMaps")) { foreach (var operation in m_WriteData.WriteOperations) { var internalName = operation.Command.internalName; BuildReferenceMap referenceMap = m_WriteData.FileToReferenceMap[internalName]; if (!forwardObjectDependencies.TryGetValue(internalName, out var objectDependencies)) { continue; // this bundle has no external dependencies } if (!forwardFileDependencies.TryGetValue(internalName, out var fileDependencies)) { continue; // this bundle has no external dependencies } foreach (string file in fileDependencies) { WriteCommand dependentCommand = fileToCommand[file]; foreach (var serializedObject in dependentCommand.serializeObjects) { // Only add objects we are referencing. This ensures that new/removed objects to files we depend upon will not cause a rebuild // of this file, unless are referencing the new/removed objects. if (!objectDependencies.Contains(serializedObject.serializationObject)) { continue; } referenceMap.AddMapping(file, serializedObject.serializationIndex, serializedObject.serializationObject); } } } } // Using the previously generate reverse dependency map, create the BuildUsageTagSet per WriteCommand to contain just the data that we care about using (m_Log.ScopedStep(LogLevel.Info, $"Populate BuildUsageTagSet")) { foreach (var operation in m_WriteData.WriteOperations) { var internalName = operation.Command.internalName; BuildUsageTagSet fileUsage = m_WriteData.FileToUsageSet[internalName]; if (reverseAssetDependencies.TryGetValue(internalName, out var assetDependencies)) { foreach (GUID asset in assetDependencies) { if (m_DependencyData.AssetUsage.TryGetValue(asset, out var assetUsage)) { fileUsage.UnionWith(assetUsage); } if (m_DependencyData.SceneUsage.TryGetValue(asset, out var sceneUsage)) { fileUsage.UnionWith(sceneUsage); } } } if (ReflectionExtensions.SupportsFilterToSubset) { fileUsage.FilterToSubset(m_WriteData.FileToObjects[internalName].ToArray()); } } } return(ReturnCode.Success); }
public static ReturnCode RunCachedOperation <T>(IBuildCache cache, IBuildLogger log, IProgressTracker tracker, List <WorkItem <T> > workItems, IRunCachedCallbacks <T> cbs ) { using (log.ScopedStep(LogLevel.Info, "RunCachedOperation")) { List <CacheEntry> cacheEntries = null; List <WorkItem <T> > nonCachedItems = workItems; var cachedItems = new List <WorkItem <T> >(); for (int i = 0; i < workItems.Count; i++) { workItems[i].Index = i; } IList <CachedInfo> cachedInfo = null; if (cache != null) { using (log.ScopedStep(LogLevel.Info, "Creating Cache Entries")) for (int i = 0; i < workItems.Count; i++) { workItems[i].entry = cbs.CreateCacheEntry(workItems[i]); } cacheEntries = workItems.Select(i => i.entry).ToList(); using (log.ScopedStep(LogLevel.Info, "Load Cached Data")) cache.LoadCachedData(cacheEntries, out cachedInfo); cachedItems = workItems.Where(x => cachedInfo[x.Index] != null).ToList(); nonCachedItems = workItems.Where(x => cachedInfo[x.Index] == null).ToList(); } using (log.ScopedStep(LogLevel.Info, "Process Entries")) foreach (WorkItem <T> item in nonCachedItems) { if (!tracker.UpdateInfoUnchecked(item.StatusText)) { return(ReturnCode.Canceled); } cbs.ProcessUncached(item); } using (log.ScopedStep(LogLevel.Info, "Process Cached Entries")) foreach (WorkItem <T> item in cachedItems) { cbs.ProcessCached(item, cachedInfo[item.Index]); } foreach (WorkItem <T> item in workItems) { cbs.PostProcess(item); } if (cache != null) { List <CachedInfo> uncachedInfo; using (log.ScopedStep(LogLevel.Info, "Saving to Cache")) { using (log.ScopedStep(LogLevel.Info, "Creating Cached Infos")) uncachedInfo = nonCachedItems.Select((item) => cbs.CreateCachedInfo(item)).ToList(); cache.SaveCachedData(uncachedInfo); } } log.AddEntrySafe(LogLevel.Info, $"Total Entries: {workItems.Count}, Processed: {nonCachedItems.Count}, Cached: {cachedItems.Count}"); return(ReturnCode.Success); } }