Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added options to synch package directory and choose clone location. #81

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 128 additions & 23 deletions ParrelSync/Editor/ClonesManager.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.IO;
using ParrelSync.Editor;
using Debug = UnityEngine.Debug;

namespace ParrelSync
Expand Down Expand Up @@ -52,7 +54,7 @@ public class ClonesManager
/// Creates clone from the project currently open in Unity Editor.
/// </summary>
/// <returns></returns>
public static Project CreateCloneFromCurrent()
public static Project CreateCloneFromCurrent(string targetProjectPath = null)
{
if (IsClone())
{
Expand All @@ -61,30 +63,41 @@ public static Project CreateCloneFromCurrent()
}

string currentProjectPath = ClonesManager.GetCurrentProjectPath();
return ClonesManager.CreateCloneFromPath(currentProjectPath);
return ClonesManager.CreateCloneFromPath(currentProjectPath, targetProjectPath);
}

/// <summary>
/// Creates clone of the project located at the given path.
/// </summary>
/// <param name="sourceProjectPath"></param>
/// <returns></returns>
public static Project CreateCloneFromPath(string sourceProjectPath)
public static Project CreateCloneFromPath(string sourceProjectPath, string targetProjectPath = null)
{
Project sourceProject = new Project(sourceProjectPath);

string cloneProjectPath = null;

//Find available clone suffix id
for (int i = 0; i < MaxCloneProjectCount; i++)

// Try to use targetProjectPath, the targetProjectPath must be an empty directory.
if (Directory.Exists(targetProjectPath)
&& File.GetAttributes(targetProjectPath).HasFlag(FileAttributes.Directory)
&& Directory.GetFiles(targetProjectPath).Length == 0)
{
string originalProjectPath = ClonesManager.GetCurrentProject().projectPath;
string possibleCloneProjectPath = originalProjectPath + ClonesManager.CloneNameSuffix + "_" + i;
cloneProjectPath = targetProjectPath;
}

if (!Directory.Exists(possibleCloneProjectPath))
if (cloneProjectPath == null)
{
//Find available clone suffix id
for (int i = 0; i < MaxCloneProjectCount; i++)
{
cloneProjectPath = possibleCloneProjectPath;
break;
string originalProjectPath = ClonesManager.GetCurrentProject().projectPath;
string possibleCloneProjectPath = originalProjectPath + ClonesManager.CloneNameSuffix + "_" + i;

if (!Directory.Exists(possibleCloneProjectPath))
{
cloneProjectPath = possibleCloneProjectPath;
break;
}
}
}

Expand All @@ -104,18 +117,40 @@ public static Project CreateCloneFromPath(string sourceProjectPath)
Debug.Log("Library copy: " + cloneProject.libraryPath);
ClonesManager.CopyDirectoryWithProgressBar(sourceProject.libraryPath, cloneProject.libraryPath,
"Cloning Project Library '" + sourceProject.name + "'. ");
Debug.Log("Packages copy: " + cloneProject.libraryPath);
ClonesManager.CopyDirectoryWithProgressBar(sourceProject.packagesPath, cloneProject.packagesPath,
"Cloning Project Packages '" + sourceProject.name + "'. ");


if (!Preferences.SymbolicLinkPackage.Value)
{
Debug.Log("Packages copy: " + cloneProject.libraryPath);
ClonesManager.CopyDirectoryWithProgressBar(sourceProject.packagesPath, cloneProject.packagesPath,
"Cloning Project Packages '" + sourceProject.name + "'. ");
}

//Link Folders
ClonesManager.LinkFolders(sourceProject.assetPath, cloneProject.assetPath);
if (Preferences.SymbolicLinkPackage.Value)
ClonesManager.LinkFolders(sourceProject.packagesPath, cloneProject.packagesPath);
ClonesManager.LinkFolders(sourceProject.projectSettingsPath, cloneProject.projectSettingsPath);
ClonesManager.LinkFolders(sourceProject.autoBuildPath, cloneProject.autoBuildPath);
ClonesManager.LinkFolders(sourceProject.localPackages, cloneProject.localPackages);

ClonesManager.RegisterClone(cloneProject);

// Record clone project path.
ParrelSyncSettings settings = AssetDatabase.LoadAssetAtPath<ParrelSyncSettings>("Assets/Editor/ParrelSyncSettings.asset");
if (settings == null)
{
settings = ScriptableObject.CreateInstance<ParrelSyncSettings>();

MigrateOldCloneList(settings);

SetupSettingsDirectory();

AssetDatabase.CreateAsset(settings, "Assets/Editor/ParrelSyncSettings.asset");
AssetDatabase.SaveAssets();
}

settings.cloneProjectPath.Add(cloneProject.projectPath);
EditorUtility.SetDirty(settings);
AssetDatabase.SaveAssets();

return cloneProject;
}
Expand Down Expand Up @@ -264,6 +299,21 @@ public static void DeleteClone(string cloneProjectPath)
Debug.LogWarning("Not in a known editor. Where are you!?");
break;
}

ParrelSyncSettings settings = AssetDatabase.LoadAssetAtPath<ParrelSyncSettings>("Assets/Editor/ParrelSyncSettings.asset");
if (settings == null)
{
Debug.LogError("ParrelSyncSettings not found, this delete operation should not be triggered.");
return;
}

if (!settings.cloneProjectPath.Contains(cloneProjectPath))
{
Debug.LogError("Removed path not found in settings.");
return;
}

settings.cloneProjectPath.Remove(cloneProjectPath);
}

#endregion
Expand Down Expand Up @@ -428,6 +478,28 @@ public static Project GetCurrentProject()
return new Project(pathString);
}

/// <summary>
/// Get a new default clone project name.
/// </summary>
/// <returns>a new default name.</returns>
/// <exception cref="IndexOutOfRangeException">When exceed max MaxCloneProjectCount</exception>
public static string GetDefaultCloneProjectName()
{
//Find available clone suffix id
for (int i = 0; i < MaxCloneProjectCount; i++)
{
string originalProjectPath = ClonesManager.GetCurrentProject().projectPath;
string possibleCloneProjectPath = originalProjectPath + ClonesManager.CloneNameSuffix + "_" + i;

if (!Directory.Exists(possibleCloneProjectPath))
{
return possibleCloneProjectPath;
}
}

throw new IndexOutOfRangeException("Exceed max clone project count.");
}

/// <summary>
/// Get the argument of this clone project.
/// If this is the original project, will return an empty string.
Expand Down Expand Up @@ -484,17 +556,50 @@ public static string GetOriginalProjectPath()
/// <returns></returns>
public static List<string> GetCloneProjectsPath()
{
List<string> projectsPath = new List<string>();
ParrelSyncSettings settings = AssetDatabase.LoadAssetAtPath<ParrelSyncSettings>("Assets/Editor/ParrelSyncSettings.asset");
if (settings == null)
{
settings = ScriptableObject.CreateInstance<ParrelSyncSettings>();

MigrateOldCloneList(settings);

SetupSettingsDirectory();

AssetDatabase.CreateAsset(settings, "Assets/Editor/ParrelSyncSettings.asset");
AssetDatabase.SaveAssets();
}

return settings.cloneProjectPath;
}

/// <summary>
/// Setup the directory to store settings file.
/// </summary>
private static void SetupSettingsDirectory()
{
if (!AssetDatabase.IsValidFolder("Assets/Editor"))
{
AssetDatabase.CreateFolder("Assets", "Editor");
}
}

/// <summary>
/// Migrate the old clone project path to new project path list in the settings.
/// </summary>
/// <param name="settings">the settings instance of the project.</param>
private static void MigrateOldCloneList(ParrelSyncSettings settings)
{
//Find available clone suffix id
for (int i = 0; i < MaxCloneProjectCount; i++)
{
string originalProjectPath = ClonesManager.GetCurrentProject().projectPath;
string cloneProjectPath = originalProjectPath + ClonesManager.CloneNameSuffix + "_" + i;
string possibleCloneProjectPath = originalProjectPath + ClonesManager.CloneNameSuffix + "_" + i;

if (Directory.Exists(cloneProjectPath))
projectsPath.Add(cloneProjectPath);
if (Directory.Exists(possibleCloneProjectPath))
{
settings.cloneProjectPath.Add(possibleCloneProjectPath);
}
}

return projectsPath;
}

/// <summary>
Expand Down
54 changes: 51 additions & 3 deletions ParrelSync/Editor/ClonesManagerWindow.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using UnityEngine;
using System;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Linq;

namespace ParrelSync
{
Expand Down Expand Up @@ -177,7 +179,7 @@ private void OnGUI()

if (GUILayout.Button("Add new clone"))
{
ClonesManager.CreateCloneFromCurrent();
CloneProject();
}

GUILayout.EndVertical();
Expand All @@ -189,10 +191,56 @@ private void OnGUI()
EditorGUILayout.HelpBox("No project clones found. Create a new one!", MessageType.Info);
if (GUILayout.Button("Create new clone"))
{
ClonesManager.CreateCloneFromCurrent();
CloneProject();
}
}
}
}

/// <summary>
/// Wrapper method to create a new clone.
/// </summary>
private void CloneProject()
{
string parentPath = Directory.GetParent(ClonesManager.GetCurrentProjectPath())?.FullName;
string defaultClonePath = ClonesManager.GetDefaultCloneProjectName();
string projectName = defaultClonePath.Split('/').Last();

string selectedPath = null;
if (EditorUtility.DisplayDialog("Using Default Clone Path?",
$"Do you want to create a clone at {defaultClonePath}?",
"Yes",
"No, I want select my own path"))
{
ClonesManager.CreateCloneFromCurrent(defaultClonePath);
return;
}

selectedPath = EditorUtility.SaveFolderPanel("Clone Project Location", parentPath, projectName);
if (string.IsNullOrEmpty(selectedPath))
{
Debug.Log("Clone canceled.");
return;
}

if (!IsEmptyDirectory(selectedPath))
{
Debug.Log("Not an empty directory.");
EditorUtility.DisplayDialog("Invalid Directory", "Selected directory is not empty.", "OK");
return;
}

ClonesManager.CreateCloneFromCurrent(selectedPath);
}

private bool IsEmptyDirectory(string path)
{
if (!Directory.Exists(path))
{
throw new ArgumentException("Directory not found.");
}

return (Directory.GetFiles(path).Length == 0 && Directory.GetDirectories(path).Length == 0);
}
}
}
11 changes: 11 additions & 0 deletions ParrelSync/Editor/ParrelSyncSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;

namespace ParrelSync.Editor
{
public class ParrelSyncSettings : ScriptableObject
{
public List<string> cloneProjectPath = new List<string>();
}
}
3 changes: 3 additions & 0 deletions ParrelSync/Editor/ParrelSyncSettings.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions ParrelSync/Editor/Preferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ private static void InitWindow()
/// </summary>
public static BoolPreference AlsoCheckUnityLockFileStaPref = new BoolPreference("ParrelSync_CheckUnityLockFileOpenStatus", true);

/// <summary>
/// If create a symbolic link instead of copy content for project Package directory.
/// </summary>
public static BoolPreference SymbolicLinkPackage = new BoolPreference("ParrelSync_SymbolicLinkPackage", false);

private void OnGUI()
{
if (ClonesManager.IsClone())
Expand Down Expand Up @@ -97,11 +102,20 @@ private void OnGUI()
),
AlsoCheckUnityLockFileStaPref.Value);
}

SymbolicLinkPackage.Value = EditorGUILayout.ToggleLeft(
new GUIContent(
"Use symbolic link instead of copy content for project Package directory.",
"When enabled, a symbolic link will be created for Package directory. It is recommended if you are modifying contents inside Package directory."
),
SymbolicLinkPackage.Value);

GUILayout.EndVertical();
if (GUILayout.Button("Reset to default"))
{
AssetModPref.ClearValue();
AlsoCheckUnityLockFileStaPref.ClearValue();
SymbolicLinkPackage.ClearValue();
Debug.Log("Editor preferences cleared");
}
GUILayout.EndVertical();
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# ParrelSync
[![Release](https://img.shields.io/github/v/release/VeriorPies/ParrelSync?include_prereleases)](https://github.com/VeriorPies/ParrelSync/releases) [![Documentation](https://img.shields.io/badge/documentation-brightgreen.svg)](https://github.com/VeriorPies/ParrelSync/wiki) [![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/VeriorPies/ParrelSync/blob/master/LICENSE.md) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-blue.svg)](https://github.com/VeriorPies/ParrelSync/pulls) [![Chats](https://img.shields.io/discord/710688100996743200)](https://discord.gg/TmQk2qG)
[![Release](https://img.shields.io/github/v/release/VeriorPies/ParrelSync)](https://github.com/VeriorPies/ParrelSync/releases) [![Documentation](https://img.shields.io/badge/documentation-brightgreen.svg)](https://github.com/VeriorPies/ParrelSync/wiki) [![License](https://img.shields.io/badge/license-MIT-green)](https://github.com/VeriorPies/ParrelSync/blob/master/LICENSE.md) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-blue.svg)](https://github.com/VeriorPies/ParrelSync/pulls) [![Chats](https://img.shields.io/discord/710688100996743200)](https://discord.gg/TmQk2qG)

ParrelSync is a Unity editor extension that allows users to test multiplayer gameplay without building the project by having another Unity editor window opened and mirror the changes from the original project.

Expand Down