public override async Task <bool> PrepareForRun(SessionTemplateGeneratorParameters parameters) { if (parameters == null) { throw new ArgumentNullException(nameof(parameters)); } parameters.Validate(); var initialName = parameters.Name; var existingNames = ExtractReferencesList(parameters); initialName = NamingHelper.ComputeNewName(initialName, (Core.IO.UFile uf) => IsNameColliding(existingNames, uf), "{0}{1}"); var window = new ProjectLibraryWindow(initialName); window.LibNameInputValidator = (name) => IsNameColliding(existingNames, name); await window.ShowModal(); if (window.Result == DialogResult.Cancel) { return(false); } parameters.Name = Utilities.BuildValidProjectName(window.LibraryName); parameters.Namespace = Utilities.BuildValidNamespaceName(window.Namespace); var collision = IsNameColliding(existingNames, parameters.Name); return(!collision); // we cannot allow to flow the creation request in case of name collision, because the underlying viewmodel system does not have protection against it. }
private static UFile GenerateLocation(string assetName, AssetTemplateGeneratorParameters parameters) { var location = assetName.StartsWith(parameters.TargetLocation) ? new UFile(assetName) : UPath.Combine(parameters.TargetLocation, new UFile(assetName)); return(NamingHelper.ComputeNewName(location, x => parameters.Package.Assets.Find(x) != null, "{0}_{1}")); }
/// <summary> /// Creates a new sub-directory with a default name in this directory. /// </summary> /// <param name="editing">Indicates whether the new sub-directory should be put in edit mode immediately when constructed.</param> /// <returns>A new instance of <see cref="DirectoryViewModel"/> representing the new sub-directory.</returns> public DirectoryViewModel CreateSubDirectory(bool editing) { var newDirectory = new DirectoryViewModel(NamingHelper.ComputeNewName(NewFolderDefaultName, SubDirectories.Cast <DirectoryBaseViewModel>(), x => x.Name), this, true) { IsEditing = editing }; return(newDirectory); }
/// <summary> /// Finds a name available for a new asset. This method will try to create a name based on an existing name and will append /// "_" + (number++) on every try. The new location found is added to the known existing locations. /// </summary> /// <param name="location">The location.</param> /// <param name="newLocation">The new location.</param> /// <returns><c>true</c> if there is a new location, <c>false</c> otherwise.</returns> public bool RegisterLocation(UFile location, out UFile newLocation) { newLocation = location; if (IsContainingLocation(location)) { newLocation = NamingHelper.ComputeNewName(location, IsContainingLocation); } ExistingLocations.Add(newLocation); return(newLocation != location); }
protected List <AssetItem> MakeUniqueNames(IEnumerable <AssetItem> assets) { var result = new List <AssetItem>(); foreach (var asset in assets) { var uniqueName = NamingHelper.ComputeNewName(asset.Location, x => result.Any(y => y.Asset != asset.Asset && x == y.Location), "{0}_{1}"); result.Add(new AssetItem(uniqueName, asset.Asset)); } return(result); }
protected string GenerateUniqueNameAtLocation(List <string> conflictingNames = null) { if (conflictingNames == null) { conflictingNames = new List <string>(); } if (Directory.Exists(Location)) { // Also add currently existing folders var existingFolders = Directory.GetDirectories(Location).Select(Path.GetFileName); conflictingNames.AddRange(existingFolders); } // Generate a name that does not collide with one of the previously gathered names var newName = NamingHelper.ComputeNewName(SelectedTemplate.DefaultOutputName, conflictingNames, x => x, "{0}{1}"); return(newName); }
private void CreateUnitTestAsset() { #if DEBUG var package = Editor.Session.CurrentPackage; if (package != null) { using (var transaction = Editor.Session.UndoRedoService.CreateTransaction()) { var dir = package.AssetMountPoint; var name = NamingHelper.ComputeNewName("UnitTestAsset", x => dir.Assets.Any(y => string.Equals(x, y.Name, StringComparison.OrdinalIgnoreCase))); var asset = UnitTestAsset.CreateNew(); var assetItem = new AssetItem(name, asset); var assetViewModel = package.CreateAsset(dir, assetItem, true, null); Editor.Session.NotifyAssetPropertiesChanged(new[] { assetViewModel }); Editor.Session.ActiveAssetView.SelectAssets(new[] { assetViewModel }); Editor.Session.UndoRedoService.SetName(transaction, $"Create test asset '{name}'"); } } #endif }
protected override string UpdateNameFromSelectedTemplate() { var selectedTemplate = SelectedTemplate?.GetTemplate() as TemplateAssetDescription; if (selectedTemplate == null || !selectedTemplate.RequireName) { return(null); } // If the mount point of the current folder does not support this type of asset, try to select the first mount point that support it. var assetType = selectedTemplate.GetAssetType(); TargetDirectory = AssetViewModel.FindValidCreationLocation(assetType, CurrentDirectory); if (TargetDirectory == null) { return(null); } var baseName = selectedTemplate.DefaultOutputName ?? selectedTemplate.AssetTypeName; var name = NamingHelper.ComputeNewName(baseName, TargetDirectory.Assets, x => x.Name, "{0}{1}"); return(name); }
public sealed override bool Run(AssetTemplateGeneratorParameters parameters) { var assets = CreateAssets(parameters)?.ToList(); if (assets == null) { parameters.Logger.Error("No asset created by the asset factory."); return(false); } // Step one: add assets to package with proper unique name var newAssets = new Dictionary <AssetId, AssetItem>(); foreach (var asset in assets) { if (string.IsNullOrEmpty(asset.Location)) { throw new InvalidOperationException($"Asset returned by {nameof(CreateAssets)} has no location. Use {nameof(GenerateLocation)} to generate the location for each asset."); } // Ensure unicity of names amongst package var name = NamingHelper.ComputeNewName(asset.Location, x => parameters.Package.Assets.Find(x) != null, "{0}_{1}"); var item = new AssetItem(name, asset.Asset); try { parameters.Package.Assets.Add(item); // Ensure the dirty flag is properly set to refresh the dependency manager item.IsDirty = true; } catch (Exception ex) { parameters.Logger.Error("Failed to create new asset from template.", ex); return(false); } newAssets.Add(item.Id, item); } // Step two: fix references in the newly added assets foreach (var asset in newAssets) { var referencesToFix = AssetReferenceAnalysis.Visit(asset.Value); foreach (var assetReferenceLink in referencesToFix) { var refToUpdate = assetReferenceLink.Reference as IReference; if (refToUpdate == null) { continue; } AssetItem realItem; // Look for the referenced asset in the new assets if (newAssets.TryGetValue(refToUpdate.Id, out realItem)) { assetReferenceLink.UpdateReference(realItem.Id, realItem.Location); } else { // If not found, try on the already existing assets realItem = parameters.Package.Session.FindAsset(refToUpdate.Id); assetReferenceLink.UpdateReference(realItem?.Id ?? AssetId.Empty, realItem?.Location); } } } // Step three: complete creation and mark them as dirty foreach (var asset in newAssets.Values) { try { PostAssetCreation(parameters, asset); // Ensure the dirty flag is properly set to refresh the dependency manager asset.IsDirty = true; } catch (Exception ex) { parameters.Logger.Error("Failed to create new asset from template.", ex); return(false); } } return(true); }
private async Task GeneratePrecompiledFont() { var font = (SpriteFontAsset)AssetItem.Asset; var dialogService = ServiceProvider.Get <IDialogService>(); // Dynamic font cannot be precompiled if (font.FontType is RuntimeRasterizedSpriteFontType) { // Note: Markdown (**, _) are used to format the text. await dialogService.MessageBox(Tr._p("Message", "**Only static fonts can be precompiled.**\r\n\r\nClear the _Is Dynamic_ property on this font and try again."), MessageBoxButton.OK, MessageBoxImage.Error); return; } // Compute unique name var precompiledName = NamingHelper.ComputeNewName($"{AssetItem.Location.GetFileNameWithoutExtension()} (Precompiled)", Directory.Assets, x => x.Name); // Ask location for generated texture var folderDialog = dialogService.CreateFolderOpenModalDialog(); folderDialog.InitialDirectory = (Session.CurrentProject?.Package?.RootDirectory ?? Session.SolutionPath.GetFullDirectory()).ToWindowsPath() + "\\Resources"; var dialogResult = await folderDialog.ShowModal(); if (dialogResult != DialogResult.Ok) { return; } bool srgb; var gameSettings = Session.CurrentProject?.Package.GetGameSettingsAsset(); if (gameSettings == null) { var buttons = DialogHelper.CreateButtons(new[] { ColorSpace.Linear.ToString(), ColorSpace.Gamma.ToString(), Tr._p("Button", "Cancel") }, 1, 3); var result = await dialogService.MessageBox(Tr._p("Message", "Which color space do you want to use?"), buttons, MessageBoxImage.Question); // Close without clicking a button or Cancel if (result == 0 || result == 3) { return; } srgb = result == 2; } else { srgb = gameSettings.GetOrCreate <RenderingSettings>().ColorSpace == ColorSpace.Linear; } var precompiledFontAsset = (font.FontType is SignedDistanceFieldSpriteFontType) ? font.GeneratePrecompiledSDFSpriteFont(AssetItem, UFile.Combine(folderDialog.Directory, precompiledName)) : font.GeneratePrecompiledSpriteFont(AssetItem, UFile.Combine(folderDialog.Directory, precompiledName), srgb); // NOTE: following code could be factorized with AssetFactoryViewModel var defaultLocation = UFile.Combine(Directory.Path, precompiledName); var assetItem = new AssetItem(defaultLocation, precompiledFontAsset); AssetViewModel assetViewModel; using (var transaction = UndoRedoService.CreateTransaction()) { // FIXME: do we need to delete the generated file upon undo? assetViewModel = Directory.Package.CreateAsset(Directory, assetItem, true, null); UndoRedoService.SetName(transaction, $"Create Asset '{precompiledName}'"); } Session.CheckConsistency(); if (assetViewModel != null) { Session.ActiveAssetView.SelectAssetCommand.Execute(assetViewModel); } }
/// <summary> /// Checks if a default scene exists for this game package. /// </summary> /// <param name="log">The log to output the result of the validation.</param> public override void Run(ILogger log) { if (log == null) { throw new ArgumentNullException(nameof(log)); } foreach (var package in Session.Packages) { // Make sure package has its assets loaded if (package.State < PackageState.AssetsReady) { continue; } var hasGameExecutable = package.Profiles.SelectMany(profile => profile.ProjectReferences).Any(projectRef => projectRef.Type == ProjectType.Executable); if (!hasGameExecutable) { continue; } // Find game settings var gameSettingsAssetItem = package.Assets.Find(GameSettingsAsset.GameSettingsLocation); AssetItem defaultScene = null; // If game settings is found, try to find default scene inside var defaultSceneRuntime = ((GameSettingsAsset)gameSettingsAssetItem?.Asset)?.DefaultScene; var defaultSceneReference = AttachedReferenceManager.GetAttachedReference(defaultSceneRuntime); if (defaultSceneReference != null) { // Find it either by Url or Id defaultScene = package.Assets.Find(defaultSceneReference.Id) ?? package.Assets.Find(defaultSceneReference.Url); // Check it is actually a scene asset if (defaultScene != null && !(defaultScene.Asset is SceneAsset)) { defaultScene = null; } } // Find or create default scene if (defaultScene == null) { defaultScene = package.Assets.Find(GameSettingsAsset.DefaultSceneLocation); if (defaultScene != null && !(defaultScene.Asset is SceneAsset)) { defaultScene = null; } } // Otherwise, try to find any scene if (defaultScene == null) { defaultScene = package.Assets.FirstOrDefault(x => x.Asset is SceneAsset); } // Nothing found, let's create an empty one if (defaultScene == null) { log.Error(package, null, AssetMessageCode.DefaultSceneNotFound, null); var defaultSceneName = NamingHelper.ComputeNewName(GameSettingsAsset.DefaultSceneLocation, package.Assets, a => a.Location); var defaultSceneAsset = DefaultAssetFactory <SceneAsset> .Create(); defaultScene = new AssetItem(defaultSceneName, defaultSceneAsset); package.Assets.Add(defaultScene); defaultScene.IsDirty = true; } // Create game settings if not done yet if (gameSettingsAssetItem == null) { log.Error(package, null, AssetMessageCode.AssetForPackageNotFound, GameSettingsAsset.GameSettingsLocation, package.FullPath.GetFileName()); var gameSettingsAsset = GameSettingsFactory.Create(); gameSettingsAsset.DefaultScene = AttachedReferenceManager.CreateProxyObject <Scene>(defaultScene.Id, defaultScene.Location); gameSettingsAssetItem = new AssetItem(GameSettingsAsset.GameSettingsLocation, gameSettingsAsset); package.Assets.Add(gameSettingsAssetItem); gameSettingsAssetItem.IsDirty = true; } } }
/// <summary> /// Checks if a default scene exists for this game package. /// </summary> /// <param name="log">The log to output the result of the validation.</param> public override void Run(ILogger log) { if (log == null) { throw new ArgumentNullException("log"); } foreach (var package in Session.Packages) { var hasGameExecutable = package.Profiles.SelectMany(profile => profile.ProjectReferences).Any(projectRef => projectRef.Type == ProjectType.Executable); if (!hasGameExecutable) { continue; } var sharedProfile = package.Profiles.FindSharedProfile(); if (sharedProfile == null) { continue; } var defaultScene = sharedProfile.Properties.Get(GameSettingsAsset.DefaultScene); // If the pdxpkg does not reference any scene if (defaultScene == null) { log.Error(package, null, AssetMessageCode.DefaultSceneNotFound, null); // Creates a new default scene // Checks we don't overwrite an existing asset const string defaultSceneLocation = "MainScene"; var existingDefault = package.Assets.Find(defaultSceneLocation); if (existingDefault != null && existingDefault.Asset is SceneAsset) { // A scene at the default location already exists among the assets, let's reference it as the default scene var sceneAsset = new AssetReference <SceneAsset>(existingDefault.Id, existingDefault.Location); GameSettingsAsset.SetDefaultScene(package, sceneAsset); } else if (existingDefault != null) { // Very rare case: the default scene location is occupied by another asset which is not a scene // Compute a new default name to not overwrite the existing asset var newName = NamingHelper.ComputeNewName(defaultSceneLocation, package.Assets, a => a.Location); GameSettingsAsset.CreateAndSetDefaultScene(package, newName); } else { // Creates a new default scene asset GameSettingsAsset.CreateAndSetDefaultScene(package, defaultSceneLocation); } continue; } // The pdxpkg references an asset var defaultAsset = package.Assets.Find(defaultScene.Location); if (defaultAsset != null) { continue; // Default scene exists and is referenced } // The asset referenced does not exist, create it log.Error(package, defaultScene, AssetMessageCode.AssetNotFound, defaultScene); GameSettingsAsset.CreateAndSetDefaultScene(package); } }