/// <summary>
        /// Creates a symlink between destinationPath and sourcePath (Windows version).
        /// </summary>
        /// <param name="sourcePath"></param>
        /// <param name="destinationPath"></param>
        private static void CreateLinkWin(string sourcePath, string destinationPath)
        {
            string cmd = $"/C mklink /J \"{destinationPath}\" \"{sourcePath}\"";

            Debug.Log($"Windows junction: {cmd}");
            ProjectCloner.StartHiddenConsoleProcess("cmd.exe", cmd);
        }
        /// <summary>
        /// Creates a symlink between destinationPath and sourcePath (Linux version).
        /// </summary>
        /// <param name="sourcePath"></param>
        /// <param name="destinationPath"></param>
        private static void CreateLinkLunux(string sourcePath, string destinationPath)
        {
            string cmd = string.Format("-c \"ln -s {0} {1}\"", sourcePath, destinationPath);

            Debug.Log("Linux junction: " + cmd);
            ProjectCloner.StartHiddenConsoleProcess("/bin/bash", cmd);
        }
        /// <summary>
        /// Returns the path to the original project.
        /// If currently open project is the original, returns its own path.
        /// If the original project folder cannot be found, retuns an empty string.
        /// </summary>
        /// <returns></returns>
        public static string GetOriginalProjectPath()
        {
            if (IsClone())
            {
                /// If this is a clone...
                /// Original project path can be deduced by removing the suffix from the clone's path.
                string cloneProjectPath = ProjectCloner.GetCurrentProject().projectPath;

                int index = cloneProjectPath.LastIndexOf(ProjectCloner.CloneNameSuffix);
                if (index > 0)
                {
                    string originalProjectPath = cloneProjectPath.Substring(0, index);
                    if (Directory.Exists(originalProjectPath))
                    {
                        return(originalProjectPath);
                    }
                }

                return(string.Empty);
            }
            else
            {
                /// If this is the original, we return its own path.
                return(ProjectCloner.GetCurrentProjectPath());
            }
        }
        /// <summary>
        /// Calculates the size of the given directory. Displays a progress bar.
        /// </summary>
        /// <param name="directory">Directory, which size has to be calculated.</param>
        /// <param name="includeNested">If true, size will include all nested directories.</param>
        /// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param>
        /// <returns>Size of the directory in bytes.</returns>
        private static long GetDirectorySize(DirectoryInfo directory, bool includeNested = false, string progressBarPrefix = "")
        {
            EditorUtility.DisplayProgressBar($"{progressBarPrefix}Calculating size of directories...", $"Scanning '{directory.FullName}'...", 0f);

            // Calculate size of all files in directory.
            var  enumeratedFiles = directory.EnumerateFiles();
            long filesSize       = enumeratedFiles.Sum((FileInfo file) =>
            {
                // Fix which allows to exceed MAX_PATH when working with files in Windows.
                // Without it, files with full path longer than 259 characters will throw a FileNotFoundException.
                if (Application.platform == RuntimePlatform.WindowsEditor)
                {
                    file = new FileInfo($"\\\\?\\{file.FullName}");
                }

                return(file.Length);
            });

            // Calculate size of all nested directories.
            long directoriesSize = 0;

            if (includeNested)
            {
                IEnumerable <DirectoryInfo> nestedDirectories = directory.EnumerateDirectories();
                foreach (DirectoryInfo nestedDir in nestedDirectories)
                {
                    directoriesSize += ProjectCloner.GetDirectorySize(nestedDir, true, progressBarPrefix);
                }
            }

            return(filesSize + directoriesSize);
        }
Example #5
0
        /// <summary>
        /// Creates a symlink between destinationPath and sourcePath (Windows version).
        /// </summary>
        /// <param name="sourcePath"></param>
        /// <param name="destinationPath"></param>
        private static void CreateLinkWin(string sourcePath, string destinationPath)
        {
            string cmd = "/C mklink /J " + string.Format("\"{0}\" \"{1}\"", destinationPath, sourcePath);

            Debug.Log("Windows junction: " + cmd);
            ProjectCloner.StartHiddenConsoleProcess("cmd.exe", cmd);
        }
Example #6
0
        /// <summary>
        /// Returns true is the project currently open in Unity Editor is a clone.
        /// </summary>
        /// <returns></returns>
        public static bool IsClone()
        {
            /// The project is a clone if its root directory contains an empty file named ".clone".
            string cloneFilePath = Path.Combine(ProjectCloner.GetCurrentProjectPath(), ProjectCloner.CloneFileName);
            bool   isClone       = File.Exists(cloneFilePath);

            return(isClone);
        }
Example #7
0
        /// <summary>
        /// Copies directory located at sourcePath to destinationPath. Displays a progress bar.
        /// Same as the previous method, but uses recursion to copy all nested folders as well.
        /// </summary>
        /// <param name="source">Directory to be copied.</param>
        /// <param name="destination">Destination directory (created automatically if needed).</param>
        /// <param name="totalBytes">Total bytes to be copied. Calculated automatically, initialize at 0.</param>
        /// <param name="copiedBytes">To track already copied bytes. Calculated automatically, initialize at 0.</param>
        /// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param>
        private static void CopyDirectoryWithProgressBarRecursive(DirectoryInfo source, DirectoryInfo destination, ref long totalBytes, ref long copiedBytes, string progressBarPrefix = "")
        {
            /// Directory cannot be copied into itself.
            if (source.FullName.ToLower() == destination.FullName.ToLower())
            {
                Debug.LogError("Cannot copy directory into itself.");
                return;
            }

            /// Calculate total bytes, if required.
            if (totalBytes == 0)
            {
                totalBytes = ProjectCloner.GetDirectorySize(source, true, progressBarPrefix);
            }

            /// Create destination directory, if required.
            if (!Directory.Exists(destination.FullName))
            {
                Directory.CreateDirectory(destination.FullName);
            }

            /// Copy all files from the source.
            foreach (FileInfo file in source.GetFiles())
            {
                try
                {
                    file.CopyTo(Path.Combine(destination.ToString(), file.Name), true);
                }
                catch (IOException)
                {
                    /// Some files may throw IOException if they are currently open in Unity editor.
                    /// Just ignore them in such case.
                }

                /// Account the copied file size.
                copiedBytes += file.Length;

                /// Display the progress bar.
                float progress   = (float)copiedBytes / (float)totalBytes;
                bool  cancelCopy = EditorUtility.DisplayCancelableProgressBar(
                    progressBarPrefix + "Copying '" + source.FullName + "' to '" + destination.FullName + "'...",
                    "(" + (progress * 100f).ToString("F2") + "%) Copying file '" + file.Name + "'...",
                    progress);
                if (cancelCopy)
                {
                    return;
                }
            }

            /// Copy all nested directories from the source.
            foreach (DirectoryInfo sourceNestedDir in source.GetDirectories())
            {
                DirectoryInfo nextDestingationNestedDir = destination.CreateSubdirectory(sourceNestedDir.Name);
                ProjectCloner.CopyDirectoryWithProgressBarRecursive(sourceNestedDir, nextDestingationNestedDir, ref totalBytes, ref copiedBytes, progressBarPrefix);
            }
        }
Example #8
0
        /// <summary>
        /// Creates a symlink between destinationPath and sourcePath (Mac version).
        /// </summary>
        /// <param name="sourcePath"></param>
        /// <param name="destinationPath"></param>
        private static void CreateLinkMac(string sourcePath, string destinationPath)
        {
            Debug.LogWarning("This hasn't been tested yet! I am mac-less :( Please chime in on the github if it works for you.");

            string cmd = "ln " + string.Format("\"{0}\" \"{1}\"", destinationPath, sourcePath);

            Debug.Log("Mac hard link " + cmd);

            ProjectCloner.StartHiddenConsoleProcess("/bin/bash", cmd);
        }
Example #9
0
        /// <summary>
        /// Copies directory located at sourcePath to destinationPath. Displays a progress bar.
        /// </summary>
        /// <param name="source">Directory to be copied.</param>
        /// <param name="destination">Destination directory (created automatically if needed).</param>
        /// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param>
        public static void CopyDirectoryWithProgressBar(string sourcePath, string destinationPath, string progressBarPrefix = "")
        {
            var source      = new DirectoryInfo(sourcePath);
            var destination = new DirectoryInfo(destinationPath);

            long totalBytes  = 0;
            long copiedBytes = 0;

            ProjectCloner.CopyDirectoryWithProgressBarRecursive(source, destination, ref totalBytes, ref copiedBytes, progressBarPrefix);
            EditorUtility.ClearProgressBar();
        }
Example #10
0
        /// <summary>
        /// Copies the full contents of the unity library. We want to do this to avoid the lengthy reserialization of the whole project when it opens up the clone.
        /// </summary>
        /// <param name="sourceProject"></param>
        /// <param name="destinationProject"></param>
        public static void CopyLibraryFolder(Project sourceProject, Project destinationProject)
        {
            if (Directory.Exists(destinationProject.libraryPath))
            {
                Debug.LogWarning("Library copy: destination path already exists! ");
                return;
            }

            Debug.Log("Library copy: " + destinationProject.libraryPath);
            ProjectCloner.CopyDirectoryWithProgressBar(sourceProject.libraryPath, destinationProject.libraryPath, "Cloning project '" + sourceProject.name + "'. ");
        }
        /// <summary>
        /// Copies the full contents of the unity library. We want to do this to avoid the lengthy reserialization of the whole project when it opens up the clone.
        /// </summary>
        /// <remarks>
        /// Excludes PackageCache directory, as it can cause long path exceptions on Windows if packages with long names are present.
        /// </remarks>
        /// <param name="sourceProject"></param>
        /// <param name="destinationProject"></param>
        public static void CopyLibraryFolder(Project sourceProject, Project destinationProject)
        {
            if (Directory.Exists(destinationProject.libraryPath))
            {
                Debug.LogWarning("Will not copy Library folder: destination path already exists.");
                return;
            }

            Debug.Log($"Copying Library folder '{destinationProject.libraryPath}'...");
            ProjectCloner.CopyDirectoryWithProgressBar(sourceProject.libraryPath, destinationProject.libraryPath, $"Cloning project '{sourceProject.name}'. ");
            Debug.Log("Copied Library folder.");
        }
Example #12
0
        /// <summary>
        /// Creates clone from the project currently open in Unity Editor.
        /// </summary>
        /// <returns></returns>
        public static Project CreateCloneFromCurrent()
        {
            if (IsClone())
            {
                Debug.LogError("This project is already a clone. Cannot clone it.");
                return(null);
            }

            string currentProjectPath = ProjectCloner.GetCurrentProjectPath();

            return(ProjectCloner.CreateCloneFromPath(currentProjectPath));
        }
Example #13
0
        /// <summary>
        /// Returns all clone projects path.
        /// </summary>
        /// <returns></returns>
        public static List <string> GetCloneProjectsPath()
        {
            List <string> projectsPath = new List <string>();

            for (int i = 0; i < MaxCloneProjectCount; i++)
            {
                string originalProjectPath = ProjectCloner.GetCurrentProject().projectPath;
                string cloneProjectPath    = originalProjectPath + ProjectCloner.CloneNameSuffix + "_" + i;

                if (Directory.Exists(cloneProjectPath))
                {
                    projectsPath.Add(cloneProjectPath);
                }
            }
            return(projectsPath);
        }
Example #14
0
        /// <summary>
        /// Deletes the clone of the currently open project, if such exists.
        /// </summary>
        public static void DeleteClone()
        {
            /// Clone won't be able to delete itself.
            if (ProjectCloner.IsClone())
            {
                return;
            }

            string cloneProjectPath = ProjectCloner.GetCloneProjectPath();

            ///Extra precautions.
            if (cloneProjectPath == string.Empty)
            {
                return;
            }
            if (cloneProjectPath == ProjectCloner.GetOriginalProjectPath())
            {
                return;
            }
            if (!cloneProjectPath.EndsWith(ProjectCloner.CloneNameSuffix))
            {
                return;
            }

            //Check what OS is
            switch (Application.platform)
            {
            case (RuntimePlatform.WindowsEditor):
                string args = "/c " + @"rmdir /s/q " + string.Format("\"{0}\"", cloneProjectPath);
                StartHiddenConsoleProcess("cmd.exe", args);
                Debug.Log("Clone deleted");
                break;

            case (RuntimePlatform.OSXEditor):
                throw new System.NotImplementedException("No Mac function implement yet :(");
                break;

            case (RuntimePlatform.LinuxEditor):
                throw new System.NotImplementedException("No linux support yet :(");
                break;

            default:
                Debug.LogWarning("Not in a known editor. Where are you!?");
                break;
            }
        }
Example #15
0
        /// <summary>
        /// Deletes the clone of the currently open project, if such exists.
        /// </summary>
        public static void DeleteClone()
        {
            /// Clone won't be able to delete itself.
            if (ProjectCloner.IsClone())
            {
                return;
            }

            string cloneProjectPath = ProjectCloner.GetCloneProjectPath();

            ///Extra precautions.
            if (cloneProjectPath == string.Empty)
            {
                return;
            }
            if (cloneProjectPath == ProjectCloner.GetOriginalProjectPath())
            {
                return;
            }
            if (cloneProjectPath.EndsWith(ProjectCloner.CloneNameSuffix))
            {
                return;
            }

            /// Delete the clone project folder.
            throw new System.NotImplementedException();
            // TODO: implement proper project deletion;
            //       appears that using FileUtil.DeleteFileOrDirectory(...) on symlinks affects the contents of linked folders
            //       (because this script self-deleted itself and half of the Assets folder when I tested it :D)
            //       there must be another, safe method to delete the clone folder and symlinks without touching the original
            {
                /*
                 * EditorUtility.DisplayProgressBar("Deleting clone...", "Deleting '" + ProjectCloner.GetCloneProjectPath() + "'", 0f);
                 * try
                 * {
                 *   FileUtil.DeleteFileOrDirectory(cloneProjectPath);
                 * }
                 * catch (IOException)
                 * {
                 *   EditorUtility.DisplayDialog("Could not delete clone", "'" + ProjectCloner.GetCurrentProject().name + "_clone' may be currently open in another unity Editor. Please close it and try again.", "OK");
                 * }
                 * EditorUtility.ClearProgressBar();
                 */
            }
        }
Example #16
0
        public static void initGUI()
        {
            string  projectPath    = ProjectCloner.FindCurrentProjectPath();
            Project currentProject = new Project(projectPath);
            Project nextProject    = new Project(projectPath + "_clone");

            Debug.Log("Start project:\n" + currentProject);
            Debug.Log("Clone project:\n" + nextProject);


            ProjectCloner.CreateProjectFolder(nextProject);
            ProjectCloner.CopyLibrary(currentProject, nextProject);

            ProjectCloner.linkFolders(currentProject.assetPath, nextProject.assetPath);
            ProjectCloner.linkFolders(currentProject.projectSettingsPath, nextProject.projectSettingsPath);
            ProjectCloner.linkFolders(currentProject.packagesPath, nextProject.packagesPath);
            ProjectCloner.linkFolders(currentProject.autoBuildPath, nextProject.autoBuildPath);
        }
Example #17
0
        /// <summary>
        /// Opens a project located at the given path (if one exists).
        /// </summary>
        /// <param name="projectPath"></param>
        public static void OpenProject(string projectPath)
        {
            if (!Directory.Exists(projectPath))
            {
                Debug.LogError("Cannot open the project - provided folder (" + projectPath + ") does not exist.");
                return;
            }
            if (projectPath == ProjectCloner.GetCurrentProjectPath())
            {
                Debug.LogError("Cannot open the project - it is already open.");
                return;
            }

            string fileName = EditorApplication.applicationPath;
            string args     = "-projectPath \"" + projectPath + "\"";

            Debug.Log("Opening project \"" + fileName + " " + args + "\"");
            ProjectCloner.StartHiddenConsoleProcess(fileName, args);
        }
Example #18
0
        /// <summary>
        /// Creates clone of the project located at the given path.
        /// </summary>
        /// <param name="sourceProjectPath"></param>
        /// <returns></returns>
        public static Project CreateCloneFromPath(string sourceProjectPath)
        {
            Project sourceProject = new Project(sourceProjectPath);
            Project cloneProject  = new Project(sourceProjectPath + ProjectCloner.CloneNameSuffix);

            Debug.Log("Start project name: " + sourceProject);
            Debug.Log("Clone project name: " + cloneProject);

            ProjectCloner.CreateProjectFolder(cloneProject);
            ProjectCloner.CopyLibraryFolder(sourceProject, cloneProject);

            ProjectCloner.LinkFolders(sourceProject.assetPath, cloneProject.assetPath);
            ProjectCloner.LinkFolders(sourceProject.projectSettingsPath, cloneProject.projectSettingsPath);
            ProjectCloner.LinkFolders(sourceProject.packagesPath, cloneProject.packagesPath);
            ProjectCloner.LinkFolders(sourceProject.autoBuildPath, cloneProject.autoBuildPath);

            ProjectCloner.RegisterClone(cloneProject);

            return(cloneProject);
        }
Example #19
0
        /// <summary>
        /// Creates clone of the project located at the given path.
        /// </summary>
        /// <param name="sourceProjectPath"></param>
        /// <returns></returns>
        public static Project CreateCloneFromPath(string sourceProjectPath)
        {
            Project sourceProject = new Project(sourceProjectPath);

            string cloneProjectPath = null;

            //Find available clone suffix id
            for (int i = 0; i < MaxCloneProjectCount; i++)
            {
                string originalProjectPath      = ProjectCloner.GetCurrentProject().projectPath;
                string possibleCloneProjectPath = originalProjectPath + ProjectCloner.CloneNameSuffix + "_" + i;

                if (!Directory.Exists(possibleCloneProjectPath))
                {
                    cloneProjectPath = possibleCloneProjectPath;
                    break;
                }
            }
            if (string.IsNullOrEmpty(cloneProjectPath))
            {
                Debug.LogError("The number of cloned projects has reach its limit. Limit: " + MaxCloneProjectCount);
                return(null);
            }

            Project cloneProject = new Project(cloneProjectPath);

            Debug.Log("Start project name: " + sourceProject);
            Debug.Log("Clone project name: " + cloneProject);

            ProjectCloner.CreateProjectFolder(cloneProject);
            ProjectCloner.CopyLibraryFolder(sourceProject, cloneProject);

            ProjectCloner.LinkFolders(sourceProject.assetPath, cloneProject.assetPath);
            ProjectCloner.LinkFolders(sourceProject.projectSettingsPath, cloneProject.projectSettingsPath);
            ProjectCloner.LinkFolders(sourceProject.packagesPath, cloneProject.packagesPath);
            ProjectCloner.LinkFolders(sourceProject.autoBuildPath, cloneProject.autoBuildPath);

            ProjectCloner.RegisterClone(cloneProject);

            return(cloneProject);
        }
Example #20
0
        /// <summary>
        /// Calculates the size of the given directory. Displays a progress bar.
        /// </summary>
        /// <param name="directory">Directory, which size has to be calculated.</param>
        /// <param name="includeNested">If true, size will include all nested directories.</param>
        /// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param>
        /// <returns>Size of the directory in bytes.</returns>
        private static long GetDirectorySize(DirectoryInfo directory, bool includeNested = false, string progressBarPrefix = "")
        {
            EditorUtility.DisplayProgressBar(progressBarPrefix + "Calculating size of directories...", "Scanning '" + directory.FullName + "'...", 0f);

            /// Calculate size of all files in directory.
            long filesSize = directory.EnumerateFiles().Sum((FileInfo file) => file.Length);

            /// Calculate size of all nested directories.
            long directoriesSize = 0;

            if (includeNested)
            {
                IEnumerable <DirectoryInfo> nestedDirectories = directory.EnumerateDirectories();
                foreach (DirectoryInfo nestedDir in nestedDirectories)
                {
                    directoriesSize += ProjectCloner.GetDirectorySize(nestedDir, true, progressBarPrefix);
                }
            }

            return(filesSize + directoriesSize);
        }
        /// <summary>
        /// Returns the path to the original project.
        /// If currently open project is the original, returns its own path.
        /// If the original project folder cannot be found, retuns an empty string.
        /// </summary>
        /// <returns></returns>
        public static string GetOriginalProjectPath()
        {
            if (IsClone())
            {
                // If this is a clone...
                // Original project path can be deduced by removing the suffix from the clone's path.
                string cloneProjectPath    = ProjectCloner.GetCurrentProject().projectPath;
                string originalProjectPath = cloneProjectPath.Remove(cloneProjectPath.Length - ProjectCloner.CloneNameSuffix.Length);

                if (Directory.Exists(originalProjectPath))
                {
                    return(originalProjectPath);
                }
                return(string.Empty);
            }
            else
            {
                // If this is the original, we return its own path.
                return(ProjectCloner.GetCurrentProjectPath());
            }
        }
Example #22
0
        /// <summary>
        /// Returns the path to the clone project.
        /// If currently open project is the clone, returns its own path.
        /// If the clone does not exist yet, retuns an empty string.
        /// </summary>
        /// <returns></returns>
        public static string GetCloneProjectPath()
        {
            if (IsClone())
            {
                /// If this is the clone, we return its own path.
                return(ProjectCloner.GetCurrentProjectPath());
            }
            else
            {
                /// If this is the original...
                /// Clone project path can be deduced by add the suffix to the original's path.
                string originalProjectPath = ProjectCloner.GetCurrentProject().projectPath;
                string cloneProjectPath    = originalProjectPath + ProjectCloner.CloneNameSuffix;

                if (Directory.Exists(cloneProjectPath))
                {
                    return(cloneProjectPath);
                }
                return(string.Empty);
            }
        }
Example #23
0
        private void OnGUI()
        {
            GUILayout.Label("Clone settings", EditorStyles.boldLabel);

            if (isClone)
            {
                /// If it is a clone project...
                string originalProjectPath = ProjectCloner.GetOriginalProjectPath();
                if (originalProjectPath == string.Empty)
                {
                    /// If original project cannot be found, display warning message.
                    string thisProjectName             = ProjectCloner.GetCurrentProject().name;
                    string supposedOriginalProjectName = ProjectCloner.GetCurrentProject().name.Replace("_clone", "");
                    EditorGUILayout.HelpBox(
                        "This project is a clone, but the link to the original seems lost.\nYou have to manually open the original and create a new clone instead of this one.\nThe original project should have a name '" + supposedOriginalProjectName + "', if it was not changed.",
                        MessageType.Warning);
                }
                else
                {
                    /// If original project is present, display some usage info.
                    EditorGUILayout.HelpBox(
                        "This project is a clone of the project '" + Path.GetFileName(originalProjectPath) + "'.\nIf you want to make changes the project files or manage clones, please open the original project through Unity Hub.",
                        MessageType.Info);
                }
            }
            else
            {
                /// If it is an original project...
                if (isCloneCreated)
                {
                    /// If clone is created, we can either open it or delete it.
                    string cloneProjectPath = ProjectCloner.GetCloneProjectPath();
                    EditorGUILayout.TextField("Clone project path", cloneProjectPath, EditorStyles.textField);
                    if (GUILayout.Button("Open clone project"))
                    {
                        ProjectCloner.OpenProject(cloneProjectPath);
                    }

                    if (GUILayout.Button("Delete clone"))
                    {
                        bool delete = EditorUtility.DisplayDialog(
                            "Delete the clone?",
                            "Are you sure you want to delete the clone project '" + ProjectCloner.GetCurrentProject().name + "_clone'? If so, you can always create a new clone from ProjectCloner window.",
                            "Delete",
                            "Cancel");
                        if (delete)
                        {
                            ProjectCloner.DeleteClone();
                            this.Repaint();
                        }
                    }
                }
                else
                {
                    /// If no clone created yet, we must create it.
                    EditorGUILayout.HelpBox("No project clones found. Create a new one!", MessageType.Info);
                    if (GUILayout.Button("Create new clone"))
                    {
                        ProjectCloner.CreateCloneFromCurrent();
                    }
                }
            }
        }
        /// <summary>
        /// Copies directory located at sourcePath to destinationPath. Displays a progress bar.
        /// Same as the previous method, but uses recursion to copy all nested folders as well.
        /// </summary>
        /// <param name="source">Directory to be copied.</param>
        /// <param name="destination">Destination directory (created automatically if needed).</param>
        /// <param name="totalBytes">Total bytes to be copied. Calculated automatically, initialize at 0.</param>
        /// <param name="copiedBytes">To track already copied bytes. Calculated automatically, initialize at 0.</param>
        /// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param>
        private static void CopyDirectoryWithProgressBarRecursive(DirectoryInfo source, DirectoryInfo destination, ref long totalBytes, ref long copiedBytes, string progressBarPrefix)
        {
            // Directory cannot be copied into itself.
            if (source.FullName.ToLower() == destination.FullName.ToLower())
            {
                Debug.LogError("Cannot copy directory into itself.");
                return;
            }

            // Calculate total bytes, if required.
            if (totalBytes == 0)
            {
                totalBytes = ProjectCloner.GetDirectorySize(source, true, progressBarPrefix);
            }

            // Create destination directory, if required.
            if (!Directory.Exists(destination.FullName))
            {
                Directory.CreateDirectory(destination.FullName);
            }

            // Copy all files from the source.
            foreach (FileInfo file in source.GetFiles())
            {
                var currentFile = file;
                // Fix which allows to exceed MAX_PATH when working with files in Windows.
                // Without it, files with full path longer than 259 characters will throw a FileNotFoundException.
                if (Application.platform == RuntimePlatform.WindowsEditor)
                {
                    currentFile = new FileInfo($"\\\\?\\{file.FullName}");
                }

                try
                {
                    currentFile.CopyTo(Path.Combine(destination.ToString(), currentFile.Name), true);
                }
                catch (IOException)
                {
                    // Some files may throw IOException if they are currently open in Unity editor.
                    // Just ignore them in such case.
                }

                // Account the copied file size.
                copiedBytes += currentFile.Length;

                // Display the progress bar.
                float progress   = (float)copiedBytes / (float)totalBytes;
                bool  cancelCopy = EditorUtility.DisplayCancelableProgressBar(
                    $"{progressBarPrefix}Copying '{source.FullName}' to '{destination.FullName}'...",
                    $"[{(progress * 100f):F2}%] Copying file '{currentFile.Name}'...",
                    progress);
                if (cancelCopy)
                {
                    return;
                }
            }

            // Copy all nested directories from the source.
            foreach (DirectoryInfo sourceNestedDir in source.GetDirectories())
            {
                DirectoryInfo nextDestingationNestedDir = destination.CreateSubdirectory(sourceNestedDir.Name);
                ProjectCloner.CopyDirectoryWithProgressBarRecursive(sourceNestedDir, nextDestingationNestedDir, ref totalBytes, ref copiedBytes, progressBarPrefix);
            }
        }
Example #25
0
        /// <summary>
        /// Return a project object that describes all the paths we need to clone it.
        /// </summary>
        /// <returns></returns>
        public static Project GetCurrentProject()
        {
            string pathString = ProjectCloner.GetCurrentProjectPath();

            return(new Project(pathString));
        }
        private void OnGUI()
        {
            if (isClone)
            {
                /// If it is a clone project...
                string originalProjectPath = ProjectCloner.GetOriginalProjectPath();
                if (originalProjectPath == string.Empty)
                {
                    /// If original project cannot be found, display warning message.
                    string thisProjectName             = ProjectCloner.GetCurrentProject().name;
                    string supposedOriginalProjectName = ProjectCloner.GetCurrentProject().name.Replace("_clone", "");
                    EditorGUILayout.HelpBox(
                        "This project is a clone, but the link to the original seems lost.\nYou have to manually open the original and create a new clone instead of this one.\nThe original project should have a name '" + supposedOriginalProjectName + "', if it was not changed.",
                        MessageType.Warning);
                }
                else
                {
                    /// If original project is present, display some usage info.
                    EditorGUILayout.HelpBox(
                        "This project is a clone of the project '" + Path.GetFileName(originalProjectPath) + "'.\nIf you want to make changes the project files or manage clones, please open the original project through Unity Hub.",
                        MessageType.Info);
                }
            }
            else
            {
                /// If it is an original project...
                if (isCloneCreated)
                {
                    GUILayout.BeginVertical("HelpBox");
                    GUILayout.Label("Clones of this Project");
                    /// If clone(s) is created, we can either open it or delete it.
                    var cloneProjectsPath = ProjectCloner.GetCloneProjectsPath();
                    for (int i = 0; i < cloneProjectsPath.Count; i++)
                    {
                        GUILayout.BeginVertical("GroupBox");
                        string cloneProjectPath = cloneProjectsPath[i];
                        EditorGUILayout.LabelField("Clone " + i);
                        EditorGUILayout.TextField("Clone project path", cloneProjectPath, EditorStyles.textField);
                        if (GUILayout.Button("Open in New Editor"))
                        {
                            ProjectCloner.OpenProject(cloneProjectPath);
                        }
                        GUILayout.BeginHorizontal();
                        if (GUILayout.Button("Delete"))
                        {
                            bool delete = EditorUtility.DisplayDialog(
                                "Delete the clone?",
                                "Are you sure you want to delete the clone project '" + ProjectCloner.GetCurrentProject().name + "_clone'? If so, you can always create a new clone from ProjectCloner window.",
                                "Delete",
                                "Cancel");
                            if (delete)
                            {
                                ProjectCloner.DeleteClone(cloneProjectPath);
                            }
                        }

                        //Offer a solution to user in-case they are stuck with deleting project
                        if (GUILayout.Button("?", GUILayout.Width(30)))
                        {
                            var openUrl = EditorUtility.DisplayDialog("Can't delete clone?",
                                                                      "Sometime clone can't be deleted due to it's still being opened by another unity instance running in the background." +
                                                                      "\nYou can read this answer from ServerFault on how to find and kill the process.", "Open Answer");
                            if (openUrl)
                            {
                                Application.OpenURL("https://serverfault.com/a/537762");
                            }
                        }
                        GUILayout.EndHorizontal();
                        GUILayout.EndVertical();
                    }
                    GUILayout.EndVertical();
                    //Have difficulty with naming
                    //GUILayout.Label("Other", EditorStyles.boldLabel);
                    if (GUILayout.Button("Add new clone"))
                    {
                        ProjectCloner.CreateCloneFromCurrent();
                    }
                }
                else
                {
                    /// If no clone created yet, we must create it.
                    EditorGUILayout.HelpBox("No project clones found. Create a new one!", MessageType.Info);
                    if (GUILayout.Button("Create new clone"))
                    {
                        ProjectCloner.CreateCloneFromCurrent();
                    }
                }
            }
        }