Example #1
0
        /// <summary>
        /// Saves the current project or module to the assets directory
        ///
        /// NOTE: If the project has never been saved before a `Save As` is called instead
        /// </summary>
        public static bool Save()
        {
            var persistentObject = EditorContext.PersistentObject;

            // No `PersistenceId` means this object has never been saved, promt the user with the `Save As` instead
            if (string.IsNullOrEmpty(persistentObject.PersistenceId))
            {
                return(SaveAs());
            }

            // Use a DontSaveScope since saving may trigger the `OnWillSaveAssets` callback
            using (new UTinyModificationProcessor.DontSaveScope())
            {
                var path = UTinyPersistence.PersistObject(persistentObject);

                if (string.IsNullOrEmpty(path))
                {
                    return(false);
                }
            }

            UTinyEditorPrefs.SaveWorkspace(EditorContext.Workspace, persistentObject.PersistenceId);
            OnSaveProject?.Invoke(EditorContext.Project);
            UTinyModificationProcessor.ClearChanged();

            return(true);
        }
Example #2
0
        private void AddRegistryObjectDependency(UTinyModule module)
        {
            if (null == module)
            {
                return;
            }

            foreach (var m in module.EnumerateDependencies())
            {
                var path = UTinyPersistence.GetLocation(m);

                // Core or sample module/project don't need to be packaged, they will be included from the manifest.
                if (ShouldPackage(path))
                {
                    AddFileElements(path, false);
                }

                foreach (var asset in AssetIterator.EnumerateAssets(m))
                {
                    if (ShouldPackage(asset.AssetPath))
                    {
                        AddFileElements(asset.AssetPath, false);
                    }
                }
            }
        }
Example #3
0
        public static bool SaveAs()
        {
            var persistentObject = EditorContext.PersistentObject;
            var extension        = UTinyPersistence.GetFileExtension(persistentObject);

            var path = EditorUtility.SaveFilePanelInProject($"Save {ContextType}", persistentObject.Name, extension.Substring(1), string.Empty);

            if (string.IsNullOrEmpty(path))
            {
                return(false);
            }

            // Use a DontSaveScope since saving may trigger the `OnWillSaveAssets` callback
            using (new UTinyModificationProcessor.DontSaveScope())
            {
                // Fix-up the name
                persistentObject.Name = Path.GetFileNameWithoutExtension(path);

                // Flush the caretaker so this operation is not undoable
                EditorContext.Caretaker.Update();

                path = UTinyPersistence.PersistObjectAs(persistentObject, path);

                if (string.IsNullOrEmpty(path))
                {
                    return(false);
                }
            }

            UTinyEditorPrefs.SaveWorkspace(EditorContext.Workspace, persistentObject.PersistenceId);
            OnSaveProject?.Invoke(EditorContext.Project);
            UTinyModificationProcessor.ClearChanged();

            return(true);
        }
Example #4
0
        /// <summary>
        /// Loads the utmodule at the given file path
        /// </summary>
        /// <param name="moduleFile">Relative path to the .utmodule file</param>
        public static void LoadModule(string moduleFile)
        {
            Assert.IsFalse(EditorApplication.isPlayingOrWillChangePlaymode);

            var context  = new UTinyContext();
            var registry = context.Registry;

            UTinyPersistence.LoadModule(moduleFile, registry);

            var module = registry.FindAllBySource(UTinyRegistry.DefaultSourceIdentifier).OfType <UTinyModule>().First();

            Assert.IsNotNull(module);

            module.Name = Path.GetFileNameWithoutExtension(moduleFile);

            SetupModule(registry, module);

            var project = registry.CreateProject(UTinyId.Generate(KWorkspaceProjectName), KWorkspaceProjectName);

            project.Module = (UTinyModule.Reference)module;

            var editorContext = new UTinyEditorContext((UTinyProject.Reference)project, EditorContextType.Module, context, UTinyEditorPrefs.LoadWorkspace(project.PersistenceId));

            LoadContext(editorContext, isChanged: false);
        }
Example #5
0
        /// <summary>
        /// Creates and loads a new .utmodule
        /// @NOTE The module only exists in memory until Save() is called
        /// </summary>
        public static void NewModule()
        {
            var context  = new UTinyContext();
            var registry = context.Registry;

            UTinyPersistence.LoadAllModules(registry);

            // Create a `workspace` project to host the module for editing purposes
            var project = registry.CreateProject(UTinyId.Generate(KWorkspaceProjectName), KWorkspaceProjectName);

            // Create objects for the new module
            var module = registry.CreateModule(UTinyId.New(), "NewModule");

            // Setup initial state for the module
            module.Namespace = "module";

            SetupModule(registry, module);

            project.Module = (UTinyModule.Reference)module;

            var workspace = new UTinyEditorWorkspace();

            UTinyEditorPrefs.SaveWorkspace(workspace);

            var editorContext = new UTinyEditorContext((UTinyProject.Reference)project, EditorContextType.Module, context, workspace);

            LoadContext(editorContext, isChanged: true);
        }
Example #6
0
        /// <summary>
        /// Creates and loads a new .utproject
        /// @NOTE The project only exists in memory until Save() is called
        /// </summary>
        public static void NewProject()
        {
            Assert.IsFalse(EditorApplication.isPlayingOrWillChangePlaymode);

            var context  = new UTinyContext();
            var registry = context.Registry;

            UTinyPersistence.LoadAllModules(registry);

            // Create new objects for the project
            var project = registry.CreateProject(UTinyId.New(), "NewProject");
            var module  = registry.CreateModule(UTinyId.New(), "Main");

            // Setup the start scene
            var entityGroup    = registry.CreateEntityGroup(UTinyId.New(), "NewEntityGroup");
            var entityGroupRef = (UTinyEntityGroup.Reference)entityGroup;
            var cameraEntity   = registry.CreateEntity(UTinyId.New(), "Camera");
            var transform      = cameraEntity.AddComponent(registry.GetTransformType());

            transform.Refresh();
            var camera = cameraEntity.AddComponent(registry.GetCamera2DType());

            camera.Refresh();
            camera["clearFlags"] = new UTinyEnum.Reference(registry.GetCameraClearFlagsType().Dereference(registry), 1);
            camera.AssignPropertyFrom("backgroundColor", Color.black);
            camera["depth"] = -1.0f;
            entityGroup.AddEntityReference((UTinyEntity.Reference)cameraEntity);

            // Setup initial state for the project
            module.Options           |= UTinyModuleOptions.ProjectModule;
            module.Namespace          = "game";
            module.StartupEntityGroup = (UTinyEntityGroup.Reference)entityGroup;

            module.AddEntityGroupReference(entityGroupRef);

            project.Module = (UTinyModule.Reference)module;
            project.Settings.EmbedAssets  = true;
            project.Settings.CanvasWidth  = 1920;
            project.Settings.CanvasHeight = 1080;

            SetupProject(registry, project);

            module.AddExplicitModuleDependency((UTinyModule.Reference)registry.FindByName <UTinyModule>("UTiny.Core2D"));

            // Always include a dependency on core, math, core2d by default
            // And HTML for now, since it is the only renderer we have right now.
            module.AddExplicitModuleDependency((UTinyModule.Reference)registry.FindByName <UTinyModule>("UTiny.HTML"));

            var workspace = new UTinyEditorWorkspace
            {
                OpenedEntityGroups = { entityGroupRef },
                ActiveEntityGroup  = entityGroupRef
            };

            UTinyEditorPrefs.SaveWorkspace(workspace);

            var editorContext = new UTinyEditorContext((UTinyProject.Reference)project, EditorContextType.Project, context, workspace);

            LoadContext(editorContext, isChanged: true);
        }
Example #7
0
        public override void OnImportAsset(AssetImportContext ctx)
        {
            var asset = ScriptableObject.CreateInstance <UTModule>();

            ctx.AddObjectToAsset("asset", asset);
            ctx.SetMainObject(asset);
            UTinyPersistence.MarkAssetChanged(ctx.assetPath);
        }
Example #8
0
        private void AddRequiredFileAndFolders()
        {
            var project = UTinyEditorApplication.Project;

            if (null == project)
            {
                return;
            }
            AddFileElements(UTinyPersistence.GetLocation(project), false);
        }
        private static DirectoryInfo GetOrCreateContextDirectory(UTinyEditorContext context)
        {
            string directory;

            // [MP] @TODO: Get the actual folder where the project/solution is stored.
            if (context.ContextType == EditorContextType.Project)
            {
                directory = UTinyPersistence.GetLocation(context.Project);
            }
            else
            {
                directory = UTinyPersistence.GetLocation(context.Module);
            }

            return(Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(directory ?? "Assets/"), k_UTinyTempFolderName)));
        }
Example #10
0
        private void AddRegistryObjectDependency(UTinyProject project)
        {
            if (null == project)
            {
                return;
            }

            var path = UTinyPersistence.GetLocation(project);

            // Core or sample module/project don't need to be packaged, they will be included from the manifest.
            if (ShouldPackage(path))
            {
                AddFileElements(path, false);
            }

            foreach (var module in project.Module.Dereference(project.Registry).EnumerateDependencies())
            {
                AddRegistryObjectDependency(module);
            }
        }
Example #11
0
        /// <summary>
        /// Loads the utproject at the given file path
        /// </summary>
        /// <param name="projectFile">Relative path to the .utproject file</param>
        public static void LoadProject(string projectFile)
        {
            Assert.IsFalse(EditorApplication.isPlayingOrWillChangePlaymode);

            var context  = new UTinyContext();
            var registry = context.Registry;

            UTinyPersistence.LoadProject(projectFile, registry);

            var project = registry.FindAllByType <UTinyProject>().First();

            Assert.IsNotNull(project);

            project.Name = Path.GetFileNameWithoutExtension(projectFile);

            SetupProject(registry, project);

            var editorContext = new UTinyEditorContext((UTinyProject.Reference)project, EditorContextType.Project, context, UTinyEditorPrefs.LoadWorkspace(project.PersistenceId));

            LoadContext(editorContext, isChanged: false);
        }
        private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets,
                                                   string[] movedFromAssetPaths)
        {
            // scripted importers take care of importedAssets
            // we don't care about moved assets, only deleted ones
            foreach (var a in deletedAssets)
            {
                if (a.EndsWith(UTinyPersistence.ProjectFileExtension) ||
                    a.EndsWith(UTinyPersistence.ModuleFileExtension))
                {
                    UTinyPersistence.MarkAssetChanged(a);
                }
            }

            foreach (var a in movedAssets)
            {
                if (a.EndsWith(UTinyPersistence.ProjectFileExtension) ||
                    a.EndsWith(UTinyPersistence.ModuleFileExtension))
                {
                    UTinyPersistence.MarkAssetMoved(a);
                }
            }
        }
        /// <summary>
        /// Packages assets to `assets.js` or `Assets/*.*`
        /// </summary>
        private static void PackageAssets(UTinyBuildOptions options, UTinyBuildResults results)
        {
            var buildFolder = options.Destination;
            var binFolder   = results.BinaryFolder;

            // Export assets to the build directory
            var buildAssetsFolder = new DirectoryInfo(Path.Combine(buildFolder.FullName, "Assets"));

            buildAssetsFolder.Create();
            var export = UTinyAssetExporter.Export(options.Project, buildAssetsFolder);

            // copy assets to bin AND/OR encode assets to 'assets.js'
            var binAssetsFolder = new DirectoryInfo(Path.Combine(binFolder.FullName, "Assets"));

            binAssetsFolder.Create();

            var assetsFile = new FileInfo(Path.Combine(binFolder.FullName, KAssetsFileName));

            var writer = new UTinyCodeWriter();

            PrependGeneratedHeader(writer, options.Project.Name);

            var reportAssets     = results.BuildReport.AddChild(UTinyBuildReport.AssetsNode);
            var reportJavaScript = reportAssets.AddChild("JavaScript");

            using (var jsdoc = new UTinyJsdoc.Writer(writer))
            {
                jsdoc.Type("object");
                jsdoc.Desc("Map containing URLs for all assets.  If assets are included as base64 blobs, these will be data URLs.");
                jsdoc.Line("@example var assetUrl = UT_ASSETS[\"MyCustomAsset\"]");
            }

            long totalBase64Size = 0;

            using (writer.Scope("var UT_ASSETS ="))
            {
                var i = 0;
                foreach (var info in export)
                {
                    var reportAsset = reportAssets.AddChild(info.AssetInfo.AssetPath, 0, info.AssetInfo.Object);

                    var settings = UTinyUtility.GetAssetExportSettings(options.Project, info.AssetInfo.Object);
                    if (settings.Embedded)
                    {
                        foreach (var file in info.Exported)
                        {
                            var buffer        = File.ReadAllBytes(file.FullName);
                            var base64        = Convert.ToBase64String(buffer);
                            var fileExtension = Path.GetExtension(file.FullName).ToLower();

                            string mimeType;
                            switch (fileExtension)
                            {
                            case ".png":
                                mimeType = "image/png";
                                break;

                            case ".jpg":
                            case ".jpeg":
                                mimeType = "image/jpeg";
                                break;

                            case ".webp":
                                mimeType = "image/webp";
                                break;

                            case ".mp3":
                                mimeType = "audio/mpeg";
                                break;

                            case ".wav":
                                mimeType = "audio/wav";
                                break;

                            case ".json":
                                mimeType = "application/json";
                                break;

                            case ".ttf":
                                mimeType = "font/truetype";
                                break;

                            default:
                                Debug.LogWarningFormat("Asset {0} has unknown extension, included as text/plain in assets", file);
                                mimeType = "text/plain";
                                break;
                            }

                            var comma = i != 0 ? "," : "";
                            writer.Line($"{comma}\"{Path.GetFileNameWithoutExtension(file.Name)}\": \"data:{mimeType};base64,{base64}\"");
                            i++;

                            reportAsset.AddChild(UTinyBuildPipeline.GetRelativePath(file), Encoding.ASCII.GetBytes(base64), info.AssetInfo.Object);
                            totalBase64Size += base64.Length;

                            file.Delete();
                        }
                    }
                    else
                    {
                        foreach (var file in info.Exported)
                        {
                            var comma = i != 0 ? "," : "";
                            writer.Line($"{comma}\"{Path.GetFileNameWithoutExtension(file.Name)}\": \"Assets/{file.Name}\"");
                            i++;

                            reportAsset.AddChild(file, info.AssetInfo.Object);
                        }
                    }
                }
            }

            writer.Line();

            writer.WriteRaw("var UT_ASSETS_SETUP = ");
            {
                var registry = new UTinyRegistry();
                UTinyPersistence.LoadAllModules(registry);
                var entityGroup = UTinyAssetEntityGroupGenerator.Generate(registry, options.Project);
                EntityGroupSetupVisitor.WriteEntityGroupSetupFunction(writer, options.Project, entityGroup, false, false);
            }

            // Write `assets.js`
            File.WriteAllText(assetsFile.FullName, writer.ToString());

            reportJavaScript.Item.Size = assetsFile.Length - totalBase64Size;

            // Remaining assets are binplaced
            foreach (var info in export)
            {
                foreach (var file in info.Exported)
                {
                    if (!file.Exists)
                    {
                        // this asset has been packaged already
                        continue;
                    }

                    file.MoveTo(Path.Combine(binAssetsFolder.FullName, file.Name));
                }
            }

            // Clean up the build directory
            buildAssetsFolder.Delete(true);

            // if we have no standalone assets, cleanup
            if (binAssetsFolder.GetFiles().Length <= 0)
            {
                binAssetsFolder.Delete();
            }
        }
Example #14
0
        /// <summary>
        /// @TODO This method has too many conditionals and checks... it should be managed at a higher level
        /// </summary>
        /// <param name="registry"></param>
        /// <param name="persistenceId"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static bool Accept(IRegistry registry, out string persistenceId)
        {
            Assert.IsTrue(Exists());

            registry.Clear();
            UTinyPersistence.LoadAllModules(registry);
            registry.UnregisterAllBySource(UTinyRegistry.DefaultSourceIdentifier);

            persistenceId = null;

            using (var command = new MemoryStream())
                using (var stream = File.OpenRead(GetTempLocation().FullName))
                    using (var reader = new BinaryReader(stream))
                        using (registry.SourceIdentifierScope(UTinyRegistry.DefaultSourceIdentifier))
                        {
                            var version = reader.ReadInt32();

                            Assert.IsTrue(version > 0);

                            var type = (SaveType)reader.ReadByte();

                            switch (type)
                            {
                            case SaveType.PersistentUnchanged:
                                persistenceId = reader.ReadString();
                                return(false);

                            case SaveType.PersistentChanged:
                                persistenceId = reader.ReadString();

                                var hash = reader.ReadString();
                                if (!string.IsNullOrEmpty(hash) && !string.Equals(hash, ComputeHash(persistenceId)))
                                {
                                    // Ask the user if they want to keep their changes or reload from disc
                                    if (EditorUtility.DisplayDialog($"{UTinyConstants.ApplicationName} assets changed", $"{UTinyConstants.ApplicationName} assets have changed on disk, would you like to reload the current project?", "Yes", "No"))
                                    {
                                        return(false);
                                    }
                                }
                                break;

                            case SaveType.Temporary:
                                break;

                            default:
                                throw new ArgumentOutOfRangeException();
                            }

                            // This is to handle module editing.
                            // We want to unregister it from its current source and re-register it with the persistenceId as the scope
                            if (!string.IsNullOrEmpty(persistenceId))
                            {
                                registry.UnregisterAllBySource(persistenceId);
                            }

                            FrontEnd.Accept(stream, command);

                            command.Position = 0;
                            Serialization.CommandStream.FrontEnd.Accept(command, registry);
                        }

            foreach (var project in registry.FindAllBySource(UTinyRegistry.DefaultSourceIdentifier).OfType <UTinyProject>())
            {
                project.PersistenceId = persistenceId;
            }

            return(true);
        }
Example #15
0
        private static void Update()
        {
            if (EditorApplication.isPlayingOrWillChangePlaymode)
            {
                return;
            }

            if (null == EditorContext)
            {
                // Flush asset changes from the persistence system
                // We don't care about any changes unless we have a project loaded
                UTinyPersistence.ClearChanges();
                return;
            }

            // Poll for workspace changes
            if (null != EditorContext.Workspace && s_WorkspaceVersion != EditorContext.Workspace.Version)
            {
                UTinyEditorPrefs.SaveWorkspace(EditorContext.Workspace, EditorContext.PersistentObject.PersistenceId);
                s_WorkspaceVersion = EditorContext.Workspace.Version;
            }

            // Poll for file/asset changes
            var changes = UTinyPersistence.DetectChanges(Registry);

            if (changes.changesDetected)
            {
                var persistenceId = EditorContext.PersistentObject.PersistenceId;

                foreach (var change in changes.changedSources)
                {
                    // The currently opened project or module has been changed on disc
                    if (change.Equals(persistenceId))
                    {
                        // Ask the user if they want to keep their changes or reload from disc
                        if (EditorUtility.DisplayDialog($"{UTinyConstants.ApplicationName} assets changed", $"{UTinyConstants.ApplicationName} assets have changed on disk, would you like to reload the current project?", "Yes", "No"))
                        {
                            LoadPersistenceId(persistenceId);
                        }
                        else
                        {
                            UTinyModificationProcessor.MarkChanged();
                        }
                    }
                    else
                    {
                        // This is some other file. We assume they are in a readonly state and we silently reload the object
                        UTinyPersistence.ReloadObject(EditorContext.Registry, change);
                    }
                }

                foreach (var deletion in changes.deletedSources)
                {
                    // The currently opened project or module has been deleted on disc
                    if (deletion.Equals(persistenceId))
                    {
                        // Ask the user if they want to keep their changes or close the project
                        if (EditorUtility.DisplayDialog($"{UTinyConstants.ApplicationName} assets changed", "The current project has been deleted, would you like to close the current project?", "Yes", "No"))
                        {
                            // Force close the project
                            Close();
                        }
                        else
                        {
                            UTinyModificationProcessor.MarkChanged();
                            EditorContext.PersistentObject.PersistenceId = string.Empty;
                        }
                    }
                    else
                    {
                        // This is some other file. We assume they are in a readonly state and we silently reload the object
                        EditorContext.Registry.UnregisterAllBySource(deletion);
                    }
                }

                foreach (var moved in changes.movedSources)
                {
                    if (!moved.Equals(persistenceId))
                    {
                        continue;
                    }

                    var path  = AssetDatabase.GUIDToAssetPath(moved);
                    var asset = AssetDatabase.LoadAssetAtPath(path, typeof(UnityEngine.Object));
                    if (null != asset)
                    {
                        EditorContext.PersistentObject.Name = asset.name;
                    }
                }

                OnChangesDetected?.Invoke();
            }

            // Poll for module or project changes
            if (EditorContext.ContextType == EditorContextType.Project && (s_ProjectVersion != EditorContext.Project.Version || s_ModuleVersion != EditorContext.Module.Version))
            {
                EditorContext.Project.RefreshConfiguration();
                s_ProjectVersion = EditorContext.Project.Version;
                s_ModuleVersion  = EditorContext.Module.Version;
            }

            if (s_Save)
            {
                s_Save = false;

                // NOTE: It is possible that this call will fail
                Save();
            }
        }