Compare commits
18 Commits
e1e1f9d303
...
48b92c3705
Author | SHA1 | Date |
---|---|---|
BOT Alex | 48b92c3705 | |
BOT Alex | 8a3c164169 | |
BOTAlex | c590bc893f | |
BOTAlex | 32f5f27201 | |
BOTAlex | 5fb3197aba | |
BOTAlex | 91b3c89657 | |
Sveske_Juice | b1cda4f602 | |
Sveske_Juice | ae60d7b1b4 | |
BOTAlex | c0d9c685ca | |
BOTAlex | 81862a21f0 | |
BOTAlex | a40308bcbc | |
BOTAlex | 581ec4ecb3 | |
BOTAlex | 8ca098e6af | |
BOTAlex | 06c6c578a2 | |
BOTAlex | 7ad364eb7a | |
BOTAlex | c4eb2ff33c | |
BOTAlex | d8bde37afd | |
BOTAlex | a846550fe0 |
|
@ -0,0 +1,16 @@
|
||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: e651dbb3fbac04af2b8f5abf007ddc23, type: 3}
|
||||||
|
m_Name: DefaultNetworkPrefabs
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
IsDefault: 1
|
||||||
|
List: []
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9fbef03eb9a91c94993d09dfd53132e1
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 49376359f1ff56f4fa46e5790467b079
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,20 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2007 James Newton-King
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 61f1aa0bc5b10d65f86052b2b82f2da9
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,33 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: edb8ba1af0bfff1b9a9969aaba13dd13
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
- first:
|
||||||
|
Windows Store Apps: WindowsStoreApps
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 711bbd1e36ca42a4bad871eb6a3de1bc
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a31ea7d0315594440839cdb0db6bc411
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8b14e706b1e7cb044b23837e8a70cad9
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,22 @@
|
||||||
|
using UnityEditor;
|
||||||
|
namespace ParrelSync
|
||||||
|
{
|
||||||
|
[InitializeOnLoad]
|
||||||
|
public class EditorQuit
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Is editor being closed
|
||||||
|
/// </summary>
|
||||||
|
static public bool IsQuiting { get; private set; }
|
||||||
|
static void Quit()
|
||||||
|
{
|
||||||
|
IsQuiting = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EditorQuit()
|
||||||
|
{
|
||||||
|
IsQuiting = false;
|
||||||
|
EditorApplication.quitting += Quit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: d4d58ce9310f45c42af5d8003f1a832c
|
guid: bf2888ff90706904abc2d851c3e59e00
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
|
@ -0,0 +1,34 @@
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
namespace ParrelSync
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// For preventing assets being modified from the clone instance.
|
||||||
|
/// </summary>
|
||||||
|
public class ParrelSyncAssetModificationProcessor : UnityEditor.AssetModificationProcessor
|
||||||
|
{
|
||||||
|
public static string[] OnWillSaveAssets(string[] paths)
|
||||||
|
{
|
||||||
|
if (ClonesManager.IsClone() && Preferences.AssetModPref.Value)
|
||||||
|
{
|
||||||
|
if (paths != null && paths.Length > 0 && !EditorQuit.IsQuiting)
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayDialog(
|
||||||
|
ClonesManager.ProjectName + ": Asset modifications saving detected and blocked",
|
||||||
|
"Asset modifications saving are blocked in the clone instance. \n\n" +
|
||||||
|
"This is a clone of the original project. \n" +
|
||||||
|
"Making changes to asset files via the clone editor is not recommended. \n" +
|
||||||
|
"Please use the original editor window if you want to make changes to the project files.",
|
||||||
|
"ok"
|
||||||
|
);
|
||||||
|
foreach (var path in paths)
|
||||||
|
{
|
||||||
|
Debug.Log("Attempting to save " + path + " are blocked.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new string[0] { };
|
||||||
|
}
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 755e570bd21b39440a923056e60f1450
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,692 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using System.Linq;
|
||||||
|
using System.IO;
|
||||||
|
using Debug = UnityEngine.Debug;
|
||||||
|
|
||||||
|
namespace ParrelSync
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains all required methods for creating a linked clone of the Unity project.
|
||||||
|
/// </summary>
|
||||||
|
public class ClonesManager
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Name used for an identifying file created in the clone project directory.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// (!) Do not change this after the clone was created, because then connection will be lost.
|
||||||
|
/// </remarks>
|
||||||
|
public const string CloneFileName = ".clone";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Suffix added to the end of the project clone name when it is created.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// (!) Do not change this after the clone was created, because then connection will be lost.
|
||||||
|
/// </remarks>
|
||||||
|
public const string CloneNameSuffix = "_clone";
|
||||||
|
|
||||||
|
public const string ProjectName = "ParrelSync";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum number of clones
|
||||||
|
/// </summary>
|
||||||
|
public const int MaxCloneProjectCount = 10;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the file for storing clone's argument.
|
||||||
|
/// </summary>
|
||||||
|
public const string ArgumentFileName = ".parrelsyncarg";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default argument of the new clone
|
||||||
|
/// </summary>
|
||||||
|
public const string DefaultArgument = "client";
|
||||||
|
|
||||||
|
#region Managing clones
|
||||||
|
|
||||||
|
/// <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 = ClonesManager.GetCurrentProjectPath();
|
||||||
|
return ClonesManager.CreateCloneFromPath(currentProjectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 = ClonesManager.GetCurrentProject().projectPath;
|
||||||
|
string possibleCloneProjectPath = originalProjectPath + ClonesManager.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 cloning project, original project: " + sourceProject + ", clone project: " + cloneProject);
|
||||||
|
|
||||||
|
ClonesManager.CreateProjectFolder(cloneProject);
|
||||||
|
|
||||||
|
//Copy Folders
|
||||||
|
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 + "'. ");
|
||||||
|
|
||||||
|
|
||||||
|
//Link Folders
|
||||||
|
ClonesManager.LinkFolders(sourceProject.assetPath, cloneProject.assetPath);
|
||||||
|
ClonesManager.LinkFolders(sourceProject.projectSettingsPath, cloneProject.projectSettingsPath);
|
||||||
|
ClonesManager.LinkFolders(sourceProject.autoBuildPath, cloneProject.autoBuildPath);
|
||||||
|
ClonesManager.LinkFolders(sourceProject.localPackages, cloneProject.localPackages);
|
||||||
|
|
||||||
|
//Optional Link Folders
|
||||||
|
var optionalLinkPaths = Preferences.OptionalSymbolicLinkFolders.GetStoredValue();
|
||||||
|
var projectSettings = ParrelSyncProjectSettings.GetSerializedSettings();
|
||||||
|
var projectSettingsProperty = projectSettings.FindProperty("m_OptionalSymbolicLinkFolders");
|
||||||
|
if (projectSettingsProperty is { isArray: true, arrayElementType: "string" })
|
||||||
|
{
|
||||||
|
for (var i = 0; i < projectSettingsProperty.arraySize; ++i)
|
||||||
|
{
|
||||||
|
optionalLinkPaths.Add(projectSettingsProperty.GetArrayElementAtIndex(i).stringValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var path in optionalLinkPaths)
|
||||||
|
{
|
||||||
|
var sourceOptionalPath = sourceProjectPath + path;
|
||||||
|
var cloneOptionalPath = cloneProjectPath + path;
|
||||||
|
LinkFolders(sourceOptionalPath, cloneOptionalPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClonesManager.RegisterClone(cloneProject);
|
||||||
|
|
||||||
|
return cloneProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a clone by placing an identifying ".clone" file in its root directory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cloneProject"></param>
|
||||||
|
private static void RegisterClone(Project cloneProject)
|
||||||
|
{
|
||||||
|
/// Add clone identifier file.
|
||||||
|
string identifierFile = Path.Combine(cloneProject.projectPath, ClonesManager.CloneFileName);
|
||||||
|
File.Create(identifierFile).Dispose();
|
||||||
|
|
||||||
|
//Add argument file with default argument
|
||||||
|
string argumentFilePath = Path.Combine(cloneProject.projectPath, ClonesManager.ArgumentFileName);
|
||||||
|
File.WriteAllText(argumentFilePath, DefaultArgument, System.Text.Encoding.UTF8);
|
||||||
|
|
||||||
|
/// Add collabignore.txt to stop the clone from messing with Unity Collaborate if it's enabled. Just in case.
|
||||||
|
string collabignoreFile = Path.Combine(cloneProject.projectPath, "collabignore.txt");
|
||||||
|
File.WriteAllText(collabignoreFile, "*"); /// Make it ignore ALL files in the clone.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 == ClonesManager.GetCurrentProjectPath())
|
||||||
|
{
|
||||||
|
Debug.LogError("Cannot open the project - it is already open.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Validate (and update if needed) the "Packages" folder before opening clone project to ensure the clone project will have the
|
||||||
|
//same "compiling environment" as the original project
|
||||||
|
ValidateCopiedFoldersIntegrity.ValidateFolder(projectPath, GetOriginalProjectPath(), "Packages");
|
||||||
|
|
||||||
|
string fileName = GetApplicationPath();
|
||||||
|
string args = "-projectPath \"" + projectPath + "\"";
|
||||||
|
Debug.Log("Opening project \"" + fileName + " " + args + "\"");
|
||||||
|
ClonesManager.StartHiddenConsoleProcess(fileName, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetApplicationPath()
|
||||||
|
{
|
||||||
|
switch (Application.platform)
|
||||||
|
{
|
||||||
|
case RuntimePlatform.WindowsEditor:
|
||||||
|
return EditorApplication.applicationPath;
|
||||||
|
case RuntimePlatform.OSXEditor:
|
||||||
|
return EditorApplication.applicationPath + "/Contents/MacOS/Unity";
|
||||||
|
case RuntimePlatform.LinuxEditor:
|
||||||
|
return EditorApplication.applicationPath;
|
||||||
|
default:
|
||||||
|
throw new System.NotImplementedException("Platform has not supported yet ;(");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this project being opened by an Unity editor?
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="projectPath"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool IsCloneProjectRunning(string projectPath)
|
||||||
|
{
|
||||||
|
|
||||||
|
//Determine whether it is opened in another instance by checking the UnityLockFile
|
||||||
|
string UnityLockFilePath = new string[] { projectPath, "Temp", "UnityLockfile" }
|
||||||
|
.Aggregate(Path.Combine);
|
||||||
|
|
||||||
|
switch (Application.platform)
|
||||||
|
{
|
||||||
|
case (RuntimePlatform.WindowsEditor):
|
||||||
|
//Windows editor will lock "UnityLockfile" file when project is being opened.
|
||||||
|
//Sometime, for instance: windows editor crash, the "UnityLockfile" will not be deleted even the project
|
||||||
|
//isn't being opened, so a check to the "UnityLockfile" lock status may be necessary.
|
||||||
|
if (Preferences.AlsoCheckUnityLockFileStaPref.Value)
|
||||||
|
return File.Exists(UnityLockFilePath) && FileUtilities.IsFileLocked(UnityLockFilePath);
|
||||||
|
else
|
||||||
|
return File.Exists(UnityLockFilePath);
|
||||||
|
case (RuntimePlatform.OSXEditor):
|
||||||
|
//Mac editor won't lock "UnityLockfile" file when project is being opened
|
||||||
|
return File.Exists(UnityLockFilePath);
|
||||||
|
case (RuntimePlatform.LinuxEditor):
|
||||||
|
return File.Exists(UnityLockFilePath);
|
||||||
|
default:
|
||||||
|
throw new System.NotImplementedException("IsCloneProjectRunning: Unsupport Platfrom: " + Application.platform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes the clone of the currently open project, if such exists.
|
||||||
|
/// </summary>
|
||||||
|
public static void DeleteClone(string cloneProjectPath)
|
||||||
|
{
|
||||||
|
/// Clone won't be able to delete itself.
|
||||||
|
if (ClonesManager.IsClone()) return;
|
||||||
|
|
||||||
|
///Extra precautions.
|
||||||
|
if (cloneProjectPath == string.Empty) return;
|
||||||
|
if (cloneProjectPath == ClonesManager.GetOriginalProjectPath()) return;
|
||||||
|
|
||||||
|
//Check what OS is
|
||||||
|
string identifierFile;
|
||||||
|
string args;
|
||||||
|
switch (Application.platform)
|
||||||
|
{
|
||||||
|
case (RuntimePlatform.WindowsEditor):
|
||||||
|
Debug.Log("Attempting to delete folder \"" + cloneProjectPath + "\"");
|
||||||
|
|
||||||
|
//The argument file will be deleted first at the beginning of the project deletion process
|
||||||
|
//to prevent any further reading and writing to it(There's a File.Exist() check at the (file)editor windows.)
|
||||||
|
//If there's any file in the directory being write/read during the deletion process, the directory can't be fully removed.
|
||||||
|
identifierFile = Path.Combine(cloneProjectPath, ClonesManager.ArgumentFileName);
|
||||||
|
File.Delete(identifierFile);
|
||||||
|
|
||||||
|
args = "/c " + @"rmdir /s/q " + string.Format("\"{0}\"", cloneProjectPath);
|
||||||
|
StartHiddenConsoleProcess("cmd.exe", args);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case (RuntimePlatform.OSXEditor):
|
||||||
|
Debug.Log("Attempting to delete folder \"" + cloneProjectPath + "\"");
|
||||||
|
|
||||||
|
//The argument file will be deleted first at the beginning of the project deletion process
|
||||||
|
//to prevent any further reading and writing to it(There's a File.Exist() check at the (file)editor windows.)
|
||||||
|
//If there's any file in the directory being write/read during the deletion process, the directory can't be fully removed.
|
||||||
|
identifierFile = Path.Combine(cloneProjectPath, ClonesManager.ArgumentFileName);
|
||||||
|
File.Delete(identifierFile);
|
||||||
|
|
||||||
|
FileUtil.DeleteFileOrDirectory(cloneProjectPath);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case (RuntimePlatform.LinuxEditor):
|
||||||
|
Debug.Log("Attempting to delete folder \"" + cloneProjectPath + "\"");
|
||||||
|
identifierFile = Path.Combine(cloneProjectPath, ClonesManager.ArgumentFileName);
|
||||||
|
File.Delete(identifierFile);
|
||||||
|
|
||||||
|
FileUtil.DeleteFileOrDirectory(cloneProjectPath);
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Debug.LogWarning("Not in a known editor. Where are you!?");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Creating project folders
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an empty folder using data in the given Project object
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="project"></param>
|
||||||
|
public static void CreateProjectFolder(Project project)
|
||||||
|
{
|
||||||
|
string path = project.projectPath;
|
||||||
|
Debug.Log("Creating new empty folder at: " + path);
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copies the full contents of the unity library. We want to do this to avoid the lengthy re-serialization of the whole project when it opens up the clone.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sourceProject"></param>
|
||||||
|
/// <param name="destinationProject"></param>
|
||||||
|
[System.Obsolete]
|
||||||
|
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);
|
||||||
|
ClonesManager.CopyDirectoryWithProgressBar(sourceProject.libraryPath, destinationProject.libraryPath,
|
||||||
|
"Cloning project '" + sourceProject.name + "'. ");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Creating symlinks
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
sourcePath = sourcePath.Replace(" ", "\\ ");
|
||||||
|
destinationPath = destinationPath.Replace(" ", "\\ ");
|
||||||
|
var command = string.Format("ln -s {0} {1}", sourcePath, destinationPath);
|
||||||
|
|
||||||
|
Debug.Log("Mac hard link " + command);
|
||||||
|
|
||||||
|
ClonesManager.ExecuteBashCommand(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a symlink between destinationPath and sourcePath (Linux version).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sourcePath"></param>
|
||||||
|
/// <param name="destinationPath"></param>
|
||||||
|
private static void CreateLinkLinux(string sourcePath, string destinationPath)
|
||||||
|
{
|
||||||
|
sourcePath = sourcePath.Replace(" ", "\\ ");
|
||||||
|
destinationPath = destinationPath.Replace(" ", "\\ ");
|
||||||
|
var command = string.Format("ln -s {0} {1}", sourcePath, destinationPath);
|
||||||
|
|
||||||
|
Debug.Log("Linux Symlink " + command);
|
||||||
|
|
||||||
|
ClonesManager.ExecuteBashCommand(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
ClonesManager.StartHiddenConsoleProcess("cmd.exe", cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO(?) avoid terminal calls and use proper api stuff. See below for windows!
|
||||||
|
////https://docs.microsoft.com/en-us/windows/desktop/api/ioapiset/nf-ioapiset-deviceiocontrol
|
||||||
|
//[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
|
//private static extern bool DeviceIoControl(System.IntPtr hDevice, uint dwIoControlCode,
|
||||||
|
// System.IntPtr InBuffer, int nInBufferSize,
|
||||||
|
// System.IntPtr OutBuffer, int nOutBufferSize,
|
||||||
|
// out int pBytesReturned, System.IntPtr lpOverlapped);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a link / junction from the original project to it's clone.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sourcePath"></param>
|
||||||
|
/// <param name="destinationPath"></param>
|
||||||
|
public static void LinkFolders(string sourcePath, string destinationPath)
|
||||||
|
{
|
||||||
|
if ((Directory.Exists(destinationPath) == false) && (Directory.Exists(sourcePath) == true))
|
||||||
|
{
|
||||||
|
switch (Application.platform)
|
||||||
|
{
|
||||||
|
case (RuntimePlatform.WindowsEditor):
|
||||||
|
CreateLinkWin(sourcePath, destinationPath);
|
||||||
|
break;
|
||||||
|
case (RuntimePlatform.OSXEditor):
|
||||||
|
CreateLinkMac(sourcePath, destinationPath);
|
||||||
|
break;
|
||||||
|
case (RuntimePlatform.LinuxEditor):
|
||||||
|
CreateLinkLinux(sourcePath, destinationPath);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Debug.LogWarning("Not in a known editor. Application.platform: " + Application.platform);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Skipping Asset link, it already exists: " + destinationPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Utility methods
|
||||||
|
|
||||||
|
private static bool? isCloneFileExistCache = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the project currently open in Unity Editor is a clone.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool IsClone()
|
||||||
|
{
|
||||||
|
if (isCloneFileExistCache == null)
|
||||||
|
{
|
||||||
|
/// The project is a clone if its root directory contains an empty file named ".clone".
|
||||||
|
string cloneFilePath = Path.Combine(ClonesManager.GetCurrentProjectPath(), ClonesManager.CloneFileName);
|
||||||
|
isCloneFileExistCache = File.Exists(cloneFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool)isCloneFileExistCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the path to the current unityEditor project folder's info
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetCurrentProjectPath()
|
||||||
|
{
|
||||||
|
return Application.dataPath.Replace("/Assets", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return a project object that describes all the paths we need to clone it.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Project GetCurrentProject()
|
||||||
|
{
|
||||||
|
string pathString = ClonesManager.GetCurrentProjectPath();
|
||||||
|
return new Project(pathString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the argument of this clone project.
|
||||||
|
/// If this is the original project, will return an empty string.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetArgument()
|
||||||
|
{
|
||||||
|
string argument = "";
|
||||||
|
if (IsClone())
|
||||||
|
{
|
||||||
|
string argumentFilePath = Path.Combine(GetCurrentProjectPath(), ClonesManager.ArgumentFileName);
|
||||||
|
if (File.Exists(argumentFilePath))
|
||||||
|
{
|
||||||
|
argument = File.ReadAllText(argumentFilePath, System.Text.Encoding.UTF8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 = ClonesManager.GetCurrentProject().projectPath;
|
||||||
|
|
||||||
|
int index = cloneProjectPath.LastIndexOf(ClonesManager.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 ClonesManager.GetCurrentProjectPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 = ClonesManager.GetCurrentProject().projectPath;
|
||||||
|
string cloneProjectPath = originalProjectPath + ClonesManager.CloneNameSuffix + "_" + i;
|
||||||
|
|
||||||
|
if (Directory.Exists(cloneProjectPath))
|
||||||
|
projectsPath.Add(cloneProjectPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
|
||||||
|
ClonesManager.CopyDirectoryWithProgressBarRecursive(source, destination, ref totalBytes, ref copiedBytes,
|
||||||
|
progressBarPrefix);
|
||||||
|
EditorUtility.ClearProgressBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 = ClonesManager.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())
|
||||||
|
{
|
||||||
|
// Ensure file exists before continuing.
|
||||||
|
if (!file.Exists)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
ClonesManager.CopyDirectoryWithProgressBarRecursive(sourceNestedDir, nextDestingationNestedDir,
|
||||||
|
ref totalBytes, ref copiedBytes, progressBarPrefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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.GetFiles().Sum((FileInfo file) => file.Exists ? file.Length : 0);
|
||||||
|
|
||||||
|
/// Calculate size of all nested directories.
|
||||||
|
long directoriesSize = 0;
|
||||||
|
if (includeNested)
|
||||||
|
{
|
||||||
|
IEnumerable<DirectoryInfo> nestedDirectories = directory.GetDirectories();
|
||||||
|
foreach (DirectoryInfo nestedDir in nestedDirectories)
|
||||||
|
{
|
||||||
|
directoriesSize += ClonesManager.GetDirectorySize(nestedDir, true, progressBarPrefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filesSize + directoriesSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts process in the system console, taking the given fileName and args.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileName"></param>
|
||||||
|
/// <param name="args"></param>
|
||||||
|
private static void StartHiddenConsoleProcess(string fileName, string args)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Process.Start(fileName, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Thanks to https://github.com/karl-/unity-symlink-utility/blob/master/SymlinkUtility.cs
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command"></param>
|
||||||
|
private static void ExecuteBashCommand(string command)
|
||||||
|
{
|
||||||
|
command = command.Replace("\"", "\"\"");
|
||||||
|
|
||||||
|
var proc = new Process()
|
||||||
|
{
|
||||||
|
StartInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "/bin/bash",
|
||||||
|
Arguments = "-c \"" + command + "\"",
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
CreateNoWindow = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using (proc)
|
||||||
|
{
|
||||||
|
proc.Start();
|
||||||
|
proc.WaitForExit();
|
||||||
|
|
||||||
|
if (!proc.StandardError.EndOfStream)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogError(proc.StandardError.ReadToEnd());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OpenProjectInFileExplorer(string path)
|
||||||
|
{
|
||||||
|
System.Diagnostics.Process.Start(@path);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6148e48ed6b61d748b187d06d3687b83
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,198 @@
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace ParrelSync
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///Clones manager Unity editor window
|
||||||
|
/// </summary>
|
||||||
|
public class ClonesManagerWindow : EditorWindow
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if project clone exists.
|
||||||
|
/// </summary>
|
||||||
|
public bool isCloneCreated
|
||||||
|
{
|
||||||
|
get { return ClonesManager.GetCloneProjectsPath().Count >= 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("ParrelSync/Clones Manager", priority = 0)]
|
||||||
|
private static void InitWindow()
|
||||||
|
{
|
||||||
|
ClonesManagerWindow window = (ClonesManagerWindow)EditorWindow.GetWindow(typeof(ClonesManagerWindow));
|
||||||
|
window.titleContent = new GUIContent("Clones Manager");
|
||||||
|
window.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For storing the scroll position of clones list
|
||||||
|
/// </summary>
|
||||||
|
Vector2 clonesScrollPos;
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
/// If it is a clone project...
|
||||||
|
if (ClonesManager.IsClone())
|
||||||
|
{
|
||||||
|
//Find out the original project name and show the help box
|
||||||
|
string originalProjectPath = ClonesManager.GetOriginalProjectPath();
|
||||||
|
if (originalProjectPath == string.Empty)
|
||||||
|
{
|
||||||
|
/// If original project cannot be found, display warning message.
|
||||||
|
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.\n",
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Clone project custom argument.
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField("Arguments", GUILayout.Width(70));
|
||||||
|
if (GUILayout.Button("?", GUILayout.Width(20)))
|
||||||
|
{
|
||||||
|
Application.OpenURL(ExternalLinks.CustomArgumentHelpLink);
|
||||||
|
}
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
string argumentFilePath = Path.Combine(ClonesManager.GetCurrentProjectPath(), ClonesManager.ArgumentFileName);
|
||||||
|
//Need to be careful with file reading / writing since it will effect the deletion of
|
||||||
|
// the clone project(The directory won't be fully deleted if there's still file inside being read or write).
|
||||||
|
//The argument file will be deleted first at the beginning of the project deletion process
|
||||||
|
//to prevent any further being read and write.
|
||||||
|
//Will need to take some extra cautious if want to change the design of how file editing is handled.
|
||||||
|
if (File.Exists(argumentFilePath))
|
||||||
|
{
|
||||||
|
string argument = File.ReadAllText(argumentFilePath, System.Text.Encoding.UTF8);
|
||||||
|
string argumentTextAreaInput = EditorGUILayout.TextArea(argument,
|
||||||
|
GUILayout.Height(50),
|
||||||
|
GUILayout.MaxWidth(300)
|
||||||
|
);
|
||||||
|
File.WriteAllText(argumentFilePath, argumentTextAreaInput, System.Text.Encoding.UTF8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("No argument file found.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else// If it is an original project...
|
||||||
|
{
|
||||||
|
if (isCloneCreated)
|
||||||
|
{
|
||||||
|
GUILayout.BeginVertical("HelpBox");
|
||||||
|
GUILayout.Label("Clones of this Project");
|
||||||
|
|
||||||
|
//List all clones
|
||||||
|
clonesScrollPos =
|
||||||
|
EditorGUILayout.BeginScrollView(clonesScrollPos);
|
||||||
|
var cloneProjectsPath = ClonesManager.GetCloneProjectsPath();
|
||||||
|
for (int i = 0; i < cloneProjectsPath.Count; i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
GUILayout.BeginVertical("GroupBox");
|
||||||
|
string cloneProjectPath = cloneProjectsPath[i];
|
||||||
|
|
||||||
|
bool isOpenInAnotherInstance = ClonesManager.IsCloneProjectRunning(cloneProjectPath);
|
||||||
|
|
||||||
|
if (isOpenInAnotherInstance == true)
|
||||||
|
EditorGUILayout.LabelField("Clone " + i + " (Running)", EditorStyles.boldLabel);
|
||||||
|
else
|
||||||
|
EditorGUILayout.LabelField("Clone " + i);
|
||||||
|
|
||||||
|
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.TextField("Clone project path", cloneProjectPath, EditorStyles.textField);
|
||||||
|
if (GUILayout.Button("View Folder", GUILayout.Width(80)))
|
||||||
|
{
|
||||||
|
ClonesManager.OpenProjectInFileExplorer(cloneProjectPath);
|
||||||
|
}
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField("Arguments", GUILayout.Width(70));
|
||||||
|
if (GUILayout.Button("?", GUILayout.Width(20)))
|
||||||
|
{
|
||||||
|
Application.OpenURL(ExternalLinks.CustomArgumentHelpLink);
|
||||||
|
}
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
string argumentFilePath = Path.Combine(cloneProjectPath, ClonesManager.ArgumentFileName);
|
||||||
|
//Need to be careful with file reading/writing since it will effect the deletion of
|
||||||
|
//the clone project(The directory won't be fully deleted if there's still file inside being read or write).
|
||||||
|
//The argument file will be deleted first at the beginning of the project deletion process
|
||||||
|
//to prevent any further being read and write.
|
||||||
|
//Will need to take some extra cautious if want to change the design of how file editing is handled.
|
||||||
|
if (File.Exists(argumentFilePath))
|
||||||
|
{
|
||||||
|
string argument = File.ReadAllText(argumentFilePath, System.Text.Encoding.UTF8);
|
||||||
|
string argumentTextAreaInput = EditorGUILayout.TextArea(argument,
|
||||||
|
GUILayout.Height(50),
|
||||||
|
GUILayout.MaxWidth(300)
|
||||||
|
);
|
||||||
|
File.WriteAllText(argumentFilePath, argumentTextAreaInput, System.Text.Encoding.UTF8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("No argument file found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
EditorGUILayout.Space();
|
||||||
|
|
||||||
|
|
||||||
|
EditorGUI.BeginDisabledGroup(isOpenInAnotherInstance);
|
||||||
|
|
||||||
|
if (GUILayout.Button("Open in New Editor"))
|
||||||
|
{
|
||||||
|
ClonesManager.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 '" + ClonesManager.GetCurrentProject().name + "_clone'?",
|
||||||
|
"Delete",
|
||||||
|
"Cancel");
|
||||||
|
if (delete)
|
||||||
|
{
|
||||||
|
ClonesManager.DeleteClone(cloneProjectPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Add new clone"))
|
||||||
|
{
|
||||||
|
ClonesManager.CreateCloneFromCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
}
|
||||||
|
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"))
|
||||||
|
{
|
||||||
|
ClonesManager.CreateCloneFromCurrent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a041d83486c20b84bbf5077ddfbbca37
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,13 @@
|
||||||
|
namespace ParrelSync
|
||||||
|
{
|
||||||
|
public class ExternalLinks
|
||||||
|
{
|
||||||
|
public const string RemoteVersionURL = "https://raw.githubusercontent.com/VeriorPies/ParrelSync/master/VERSION.txt";
|
||||||
|
public const string Releases = "https://github.com/VeriorPies/ParrelSync/releases";
|
||||||
|
public const string CustomArgumentHelpLink = "https://github.com/VeriorPies/ParrelSync/wiki/Argument";
|
||||||
|
|
||||||
|
public const string GitHubHome = "https://github.com/VeriorPies/ParrelSync/";
|
||||||
|
public const string GitHubIssue = "https://github.com/VeriorPies/ParrelSync/issues";
|
||||||
|
public const string FAQ = "https://github.com/VeriorPies/ParrelSync/wiki/Troubleshooting-&-FAQs";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 65daf17fbe5101b41977305639f30c65
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,31 @@
|
||||||
|
using System.IO;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace ParrelSync
|
||||||
|
{
|
||||||
|
public class FileUtilities
|
||||||
|
{
|
||||||
|
public static bool IsFileLocked(string path)
|
||||||
|
{
|
||||||
|
FileInfo file = new FileInfo(path);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
|
||||||
|
{
|
||||||
|
stream.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
//the file is unavailable because it is:
|
||||||
|
//still being written to
|
||||||
|
//or being processed by another thread
|
||||||
|
//or does not exist (has already been processed)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//file is not locked
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 11fdc6f78f8c965499a870ca06dca6bc
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 74a7aa389726f964ab34c52e208c2a43
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,78 @@
|
||||||
|
namespace ParrelSync.NonCore
|
||||||
|
{
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A simple script to display feedback/star dialog after certain time of project being opened/re-compiled.
|
||||||
|
/// Will only pop-up once unless "Remind me next time" are chosen.
|
||||||
|
/// Removing this file from project wont effect any other functions.
|
||||||
|
/// </summary>
|
||||||
|
[InitializeOnLoad]
|
||||||
|
public class AskFeedbackDialog
|
||||||
|
{
|
||||||
|
const string InitializeOnLoadCountKey = "ParrelSync_InitOnLoadCount", StopShowingKey = "ParrelSync_StopShowFeedBack";
|
||||||
|
static AskFeedbackDialog()
|
||||||
|
{
|
||||||
|
if (EditorPrefs.HasKey(StopShowingKey)) { return; }
|
||||||
|
|
||||||
|
int InitializeOnLoadCount = EditorPrefs.GetInt(InitializeOnLoadCountKey, 0);
|
||||||
|
if (InitializeOnLoadCount > 20)
|
||||||
|
{
|
||||||
|
ShowDialog();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorPrefs.SetInt(InitializeOnLoadCountKey, InitializeOnLoadCount + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//[MenuItem("ParrelSync/(Debug)Show AskFeedbackDialog ")]
|
||||||
|
private static void ShowDialog()
|
||||||
|
{
|
||||||
|
int option = EditorUtility.DisplayDialogComplex("Do you like " + ParrelSync.ClonesManager.ProjectName + "?",
|
||||||
|
"Do you like " + ParrelSync.ClonesManager.ProjectName + "?\n" +
|
||||||
|
"If so, please don't hesitate to star it on GitHub and contribute to the project!",
|
||||||
|
"Star on GitHub",
|
||||||
|
"Close",
|
||||||
|
"Remind me next time"
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (option)
|
||||||
|
{
|
||||||
|
// First parameter.
|
||||||
|
case 0:
|
||||||
|
Debug.Log("AskFeedbackDialog: Star on GitHub selected");
|
||||||
|
EditorPrefs.SetBool(StopShowingKey, true);
|
||||||
|
EditorPrefs.DeleteKey(InitializeOnLoadCountKey);
|
||||||
|
Application.OpenURL(ExternalLinks.GitHubHome);
|
||||||
|
break;
|
||||||
|
// Second parameter.
|
||||||
|
case 1:
|
||||||
|
Debug.Log("AskFeedbackDialog: Close and never show again.");
|
||||||
|
EditorPrefs.SetBool(StopShowingKey, true);
|
||||||
|
EditorPrefs.DeleteKey(InitializeOnLoadCountKey);
|
||||||
|
break;
|
||||||
|
// Third parameter.
|
||||||
|
case 2:
|
||||||
|
Debug.Log("AskFeedbackDialog: Remind me next time");
|
||||||
|
EditorPrefs.SetInt(InitializeOnLoadCountKey, 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//Debug.Log("Close windows.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///// <summary>
|
||||||
|
///// For debug purpose
|
||||||
|
///// </summary>
|
||||||
|
//[MenuItem("ParrelSync/(Debug)Delete AskFeedbackDialog keys")]
|
||||||
|
//private static void DebugDeleteAllKeys()
|
||||||
|
//{
|
||||||
|
// EditorPrefs.DeleteKey(InitializeOnLoadCountKey);
|
||||||
|
// EditorPrefs.DeleteKey(StopShowingKey);
|
||||||
|
// Debug.Log("AskFeedbackDialog keys deleted");
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 894412a5b602e6c4ba2cf2d01f4f92b5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,26 @@
|
||||||
|
namespace ParrelSync.NonCore
|
||||||
|
{
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class OtherMenuItem
|
||||||
|
{
|
||||||
|
[MenuItem("ParrelSync/GitHub/View this project on GitHub", priority = 10)]
|
||||||
|
private static void OpenGitHub()
|
||||||
|
{
|
||||||
|
Application.OpenURL(ExternalLinks.GitHubHome);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("ParrelSync/GitHub/View FAQ", priority = 11)]
|
||||||
|
private static void OpenFAQ()
|
||||||
|
{
|
||||||
|
Application.OpenURL(ExternalLinks.FAQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MenuItem("ParrelSync/GitHub/View Issues", priority = 12)]
|
||||||
|
private static void OpenGitHubIssues()
|
||||||
|
{
|
||||||
|
Application.OpenURL(ExternalLinks.GitHubIssue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7191fa4bfa12ae749b27f73ed292eaf1
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,140 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
|
||||||
|
namespace ParrelSync
|
||||||
|
{
|
||||||
|
// With ScriptableObject derived classes, .cs and .asset filenames MUST be identical
|
||||||
|
public class ParrelSyncProjectSettings : ScriptableObject
|
||||||
|
{
|
||||||
|
private const string ParrelSyncScriptableObjectsDirectory = "Assets/Plugins/ParrelSync/ScriptableObjects";
|
||||||
|
private const string ParrelSyncSettingsPath = ParrelSyncScriptableObjectsDirectory + "/" +
|
||||||
|
nameof(ParrelSyncProjectSettings) + ".asset";
|
||||||
|
|
||||||
|
[SerializeField]
|
||||||
|
[HideInInspector]
|
||||||
|
private List<string> m_OptionalSymbolicLinkFolders;
|
||||||
|
public const string NameOfOptionalSymbolicLinkFolders = nameof(m_OptionalSymbolicLinkFolders);
|
||||||
|
|
||||||
|
private static ParrelSyncProjectSettings GetOrCreateSettings()
|
||||||
|
{
|
||||||
|
ParrelSyncProjectSettings projectSettings;
|
||||||
|
if (File.Exists(ParrelSyncSettingsPath))
|
||||||
|
{
|
||||||
|
projectSettings = AssetDatabase.LoadAssetAtPath<ParrelSyncProjectSettings>(ParrelSyncSettingsPath);
|
||||||
|
|
||||||
|
if (projectSettings == null)
|
||||||
|
Debug.LogError("File Exists, but failed to load: " + ParrelSyncSettingsPath);
|
||||||
|
|
||||||
|
return projectSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
projectSettings = CreateInstance<ParrelSyncProjectSettings>();
|
||||||
|
projectSettings.m_OptionalSymbolicLinkFolders = new List<string>();
|
||||||
|
if (!Directory.Exists(ParrelSyncScriptableObjectsDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(ParrelSyncScriptableObjectsDirectory);
|
||||||
|
}
|
||||||
|
AssetDatabase.CreateAsset(projectSettings, ParrelSyncSettingsPath);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
return projectSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SerializedObject GetSerializedSettings()
|
||||||
|
{
|
||||||
|
return new SerializedObject(GetOrCreateSettings());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ParrelSyncSettingsProvider : SettingsProvider
|
||||||
|
{
|
||||||
|
private const string MenuLocationInProjectSettings = "Project/ParrelSync";
|
||||||
|
|
||||||
|
private SerializedObject _parrelSyncProjectSettings;
|
||||||
|
|
||||||
|
private class Styles
|
||||||
|
{
|
||||||
|
public static readonly GUIContent SymlinkSectionHeading = new GUIContent("Optional Folders to Symbolically Link");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ParrelSyncSettingsProvider(string path, SettingsScope scope = SettingsScope.User)
|
||||||
|
: base(path, scope)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnActivate(string searchContext, VisualElement rootElement)
|
||||||
|
{
|
||||||
|
// This function is called when the user clicks on the ParrelSyncSettings element in the Settings window.
|
||||||
|
_parrelSyncProjectSettings = ParrelSyncProjectSettings.GetSerializedSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnGUI(string searchContext)
|
||||||
|
{
|
||||||
|
var property = _parrelSyncProjectSettings.FindProperty(ParrelSyncProjectSettings.NameOfOptionalSymbolicLinkFolders);
|
||||||
|
if (property is null || !property.isArray || property.arrayElementType != "string")
|
||||||
|
return;
|
||||||
|
|
||||||
|
var optionalFolderPaths = new List<string>(property.arraySize);
|
||||||
|
for (var i = 0; i < property.arraySize; ++i)
|
||||||
|
{
|
||||||
|
optionalFolderPaths.Add(property.GetArrayElementAtIndex(i).stringValue);
|
||||||
|
}
|
||||||
|
optionalFolderPaths.Add("");
|
||||||
|
|
||||||
|
GUILayout.BeginVertical("GroupBox");
|
||||||
|
GUILayout.Label(Styles.SymlinkSectionHeading);
|
||||||
|
GUILayout.Space(5);
|
||||||
|
var projectPath = ClonesManager.GetCurrentProjectPath();
|
||||||
|
var optionalFolderPathsIsDirty = false;
|
||||||
|
for (var i = 0; i < optionalFolderPaths.Count; ++i)
|
||||||
|
{
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField(optionalFolderPaths[i], EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
||||||
|
if (GUILayout.Button("Select", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
var result = EditorUtility.OpenFolderPanel("Select Folder to Symbolically Link...", "", "");
|
||||||
|
if (result.Contains(projectPath))
|
||||||
|
{
|
||||||
|
optionalFolderPaths[i] = result.Replace(projectPath, "");
|
||||||
|
optionalFolderPathsIsDirty = true;
|
||||||
|
}
|
||||||
|
else if (result != "")
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Symbolic Link folder must be within the project directory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (GUILayout.Button("Clear", GUILayout.Width(60)))
|
||||||
|
{
|
||||||
|
optionalFolderPaths[i] = "";
|
||||||
|
optionalFolderPathsIsDirty = true;
|
||||||
|
}
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
|
||||||
|
if (!optionalFolderPathsIsDirty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
optionalFolderPaths.RemoveAll(str => str == "");
|
||||||
|
property.arraySize = optionalFolderPaths.Count;
|
||||||
|
for (var i = 0; i < property.arraySize; ++i)
|
||||||
|
{
|
||||||
|
property.GetArrayElementAtIndex(i).stringValue = optionalFolderPaths[i];
|
||||||
|
}
|
||||||
|
_parrelSyncProjectSettings.ApplyModifiedProperties();
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the SettingsProvider
|
||||||
|
[SettingsProvider]
|
||||||
|
public static SettingsProvider CreateParrelSyncSettingsProvider()
|
||||||
|
{
|
||||||
|
return new ParrelSyncSettingsProvider(MenuLocationInProjectSettings, SettingsScope.Project)
|
||||||
|
{
|
||||||
|
keywords = GetSearchKeywordsFromGUIContentProperties<Styles>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c0011418c9d75434988a06b6df93b283
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,215 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
|
||||||
|
namespace ParrelSync
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// To add value caching for <see cref="EditorPrefs"/> functions
|
||||||
|
/// </summary>
|
||||||
|
public class BoolPreference
|
||||||
|
{
|
||||||
|
public string key { get; private set; }
|
||||||
|
public bool defaultValue { get; private set; }
|
||||||
|
public BoolPreference(string key, bool defaultValue)
|
||||||
|
{
|
||||||
|
this.key = key;
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool? valueCache = null;
|
||||||
|
|
||||||
|
public bool Value
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (valueCache == null)
|
||||||
|
valueCache = EditorPrefs.GetBool(key, defaultValue);
|
||||||
|
|
||||||
|
return (bool)valueCache;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (valueCache == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
EditorPrefs.SetBool(key, value);
|
||||||
|
valueCache = value;
|
||||||
|
Debug.Log("Editor preference updated. key: " + key + ", value: " + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearValue()
|
||||||
|
{
|
||||||
|
EditorPrefs.DeleteKey(key);
|
||||||
|
valueCache = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// To add value caching for <see cref="EditorPrefs"/> functions
|
||||||
|
/// </summary>
|
||||||
|
public class ListOfStringsPreference
|
||||||
|
{
|
||||||
|
private static string serializationToken = "|||";
|
||||||
|
public string Key { get; private set; }
|
||||||
|
public ListOfStringsPreference(string key)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
}
|
||||||
|
public List<string> GetStoredValue()
|
||||||
|
{
|
||||||
|
return this.Deserialize(EditorPrefs.GetString(Key));
|
||||||
|
}
|
||||||
|
public void SetStoredValue(List<string> strings)
|
||||||
|
{
|
||||||
|
EditorPrefs.SetString(Key, this.Serialize(strings));
|
||||||
|
}
|
||||||
|
public void ClearStoredValue()
|
||||||
|
{
|
||||||
|
EditorPrefs.DeleteKey(Key);
|
||||||
|
}
|
||||||
|
public string Serialize(List<string> data)
|
||||||
|
{
|
||||||
|
string result = string.Empty;
|
||||||
|
foreach (var item in data)
|
||||||
|
{
|
||||||
|
if (item.Contains(serializationToken))
|
||||||
|
{
|
||||||
|
Debug.LogError("Unable to serialize this value ["+item+"], it contains the serialization token ["+serializationToken+"]");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += item + serializationToken;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public List<string> Deserialize(string data)
|
||||||
|
{
|
||||||
|
return data.Split(serializationToken).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class Preferences : EditorWindow
|
||||||
|
{
|
||||||
|
[MenuItem("ParrelSync/Preferences", priority = 1)]
|
||||||
|
private static void InitWindow()
|
||||||
|
{
|
||||||
|
Preferences window = (Preferences)EditorWindow.GetWindow(typeof(Preferences));
|
||||||
|
window.titleContent = new GUIContent(ClonesManager.ProjectName + " Preferences");
|
||||||
|
window.minSize = new Vector2(550, 300);
|
||||||
|
window.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disable asset saving in clone editors?
|
||||||
|
/// </summary>
|
||||||
|
public static BoolPreference AssetModPref = new BoolPreference("ParrelSync_DisableClonesAssetSaving", true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// In addition of checking the existence of UnityLockFile,
|
||||||
|
/// also check is the is the UnityLockFile being opened.
|
||||||
|
/// </summary>
|
||||||
|
public static BoolPreference AlsoCheckUnityLockFileStaPref = new BoolPreference("ParrelSync_CheckUnityLockFileOpenStatus", true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A list of folders to create sybolic links for,
|
||||||
|
/// useful for data that lives outside of the assets folder
|
||||||
|
/// eg. Wwise project data
|
||||||
|
/// </summary>
|
||||||
|
public static ListOfStringsPreference OptionalSymbolicLinkFolders = new ListOfStringsPreference("ParrelSync_OptionalSymbolicLinkFolders");
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
if (ClonesManager.IsClone())
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox(
|
||||||
|
"This is a clone project. Please use the original project editor to change preferences.",
|
||||||
|
MessageType.Info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.BeginVertical("HelpBox");
|
||||||
|
GUILayout.Label("Preferences");
|
||||||
|
GUILayout.BeginVertical("GroupBox");
|
||||||
|
|
||||||
|
AssetModPref.Value = EditorGUILayout.ToggleLeft(
|
||||||
|
new GUIContent(
|
||||||
|
"(recommended) Disable asset saving in clone editors- require re-open clone editors",
|
||||||
|
"Disable asset saving in clone editors so all assets can only be modified from the original project editor"
|
||||||
|
),
|
||||||
|
AssetModPref.Value);
|
||||||
|
|
||||||
|
if (Application.platform == RuntimePlatform.WindowsEditor)
|
||||||
|
{
|
||||||
|
AlsoCheckUnityLockFileStaPref.Value = EditorGUILayout.ToggleLeft(
|
||||||
|
new GUIContent(
|
||||||
|
"Also check UnityLockFile lock status while checking clone projects running status",
|
||||||
|
"Disable this can slightly increase Clones Manager window performance, but will lead to in-correct clone project running status" +
|
||||||
|
"(the Clones Manager window show the clone project is still running even it's not) if the clone editor crashed"
|
||||||
|
),
|
||||||
|
AlsoCheckUnityLockFileStaPref.Value);
|
||||||
|
}
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
|
||||||
|
GUILayout.BeginVertical("GroupBox");
|
||||||
|
GUILayout.Label("Optional Folders to Symbolically Link");
|
||||||
|
GUILayout.Space(5);
|
||||||
|
|
||||||
|
// cache the current value
|
||||||
|
List<string> optionalFolderPaths = OptionalSymbolicLinkFolders.GetStoredValue();
|
||||||
|
bool optionalFolderPathsAreDirty = false;
|
||||||
|
|
||||||
|
// append a new row if full
|
||||||
|
if (optionalFolderPaths.Last() != "")
|
||||||
|
{
|
||||||
|
optionalFolderPaths.Add("");
|
||||||
|
}
|
||||||
|
|
||||||
|
var projectPath = ClonesManager.GetCurrentProjectPath();
|
||||||
|
for (int i = 0; i < optionalFolderPaths.Count; ++i)
|
||||||
|
{
|
||||||
|
GUILayout.BeginHorizontal();
|
||||||
|
EditorGUILayout.LabelField(optionalFolderPaths[i], EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
|
||||||
|
if (GUILayout.Button("Select Folder", GUILayout.Width(100)))
|
||||||
|
{
|
||||||
|
var result = EditorUtility.OpenFolderPanel("Select Folder to Symbolically Link...", "", "");
|
||||||
|
if (result.Contains(projectPath))
|
||||||
|
{
|
||||||
|
optionalFolderPaths[i] = result.Replace(projectPath,"");
|
||||||
|
optionalFolderPathsAreDirty = true;
|
||||||
|
}
|
||||||
|
else if( result != "")
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Symbolic Link folder must be within the project directory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (GUILayout.Button("Clear", GUILayout.Width(100)))
|
||||||
|
{
|
||||||
|
optionalFolderPaths[i] = "";
|
||||||
|
optionalFolderPathsAreDirty = true;
|
||||||
|
}
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// only set the preference if the value is marked dirty
|
||||||
|
if (optionalFolderPathsAreDirty)
|
||||||
|
{
|
||||||
|
optionalFolderPaths.RemoveAll(str=> str == "");
|
||||||
|
OptionalSymbolicLinkFolders.SetStoredValue(optionalFolderPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
|
||||||
|
if (GUILayout.Button("Reset to default"))
|
||||||
|
{
|
||||||
|
AssetModPref.ClearValue();
|
||||||
|
AlsoCheckUnityLockFileStaPref.ClearValue();
|
||||||
|
OptionalSymbolicLinkFolders.ClearStoredValue();
|
||||||
|
Debug.Log("Editor preferences cleared");
|
||||||
|
}
|
||||||
|
GUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 24641be1c0410a745b529e61b508679f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,112 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace ParrelSync
|
||||||
|
{
|
||||||
|
public class Project : System.ICloneable
|
||||||
|
{
|
||||||
|
public string name;
|
||||||
|
public string projectPath;
|
||||||
|
string rootPath;
|
||||||
|
public string assetPath;
|
||||||
|
public string projectSettingsPath;
|
||||||
|
public string libraryPath;
|
||||||
|
public string packagesPath;
|
||||||
|
public string autoBuildPath;
|
||||||
|
public string localPackages;
|
||||||
|
|
||||||
|
char[] separator = new char[1] { '/' };
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public Project()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize the project object by parsing its full path returned by Unity into a bunch of individual folder names and paths.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path"></param>
|
||||||
|
public Project(string path)
|
||||||
|
{
|
||||||
|
ParsePath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new object with the same settings
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public object Clone()
|
||||||
|
{
|
||||||
|
Project newProject = new Project();
|
||||||
|
newProject.rootPath = rootPath;
|
||||||
|
newProject.projectPath = projectPath;
|
||||||
|
newProject.assetPath = assetPath;
|
||||||
|
newProject.projectSettingsPath = projectSettingsPath;
|
||||||
|
newProject.libraryPath = libraryPath;
|
||||||
|
newProject.name = name;
|
||||||
|
newProject.separator = separator;
|
||||||
|
newProject.packagesPath = packagesPath;
|
||||||
|
newProject.autoBuildPath = autoBuildPath;
|
||||||
|
newProject.localPackages = localPackages;
|
||||||
|
|
||||||
|
|
||||||
|
return newProject;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the project object by renaming and reparsing it. Pass in the new name of a project, and it'll update the other member variables to match.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
public void updateNewName(string newName)
|
||||||
|
{
|
||||||
|
name = newName;
|
||||||
|
ParsePath(rootPath + "/" + name + "/Assets");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Debug override so we can quickly print out the project info.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
string printString = name + "\n" +
|
||||||
|
rootPath + "\n" +
|
||||||
|
projectPath + "\n" +
|
||||||
|
assetPath + "\n" +
|
||||||
|
projectSettingsPath + "\n" +
|
||||||
|
packagesPath + "\n" +
|
||||||
|
autoBuildPath + "\n" +
|
||||||
|
localPackages + "\n" +
|
||||||
|
libraryPath;
|
||||||
|
return (printString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParsePath(string path)
|
||||||
|
{
|
||||||
|
//Unity's Application functions return the Assets path in the Editor.
|
||||||
|
projectPath = path;
|
||||||
|
|
||||||
|
//pop off the last part of the path for the project name, keep the rest for the root path
|
||||||
|
List<string> pathArray = projectPath.Split(separator).ToList<string>();
|
||||||
|
name = pathArray.Last();
|
||||||
|
|
||||||
|
pathArray.RemoveAt(pathArray.Count() - 1);
|
||||||
|
rootPath = string.Join(separator[0].ToString(), pathArray.ToArray());
|
||||||
|
|
||||||
|
assetPath = projectPath + "/Assets";
|
||||||
|
projectSettingsPath = projectPath + "/ProjectSettings";
|
||||||
|
libraryPath = projectPath + "/Library";
|
||||||
|
packagesPath = projectPath + "/Packages";
|
||||||
|
autoBuildPath = projectPath + "/AutoBuild";
|
||||||
|
localPackages = projectPath + "/LocalPackages";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ec8d3a1577179ef44815739178cf75b4
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,60 @@
|
||||||
|
using System;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
namespace ParrelSync.Update
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A simple update checker
|
||||||
|
/// </summary>
|
||||||
|
public class UpdateChecker
|
||||||
|
{
|
||||||
|
//const string LocalVersionFilePath = "Assets/ParrelSync/VERSION.txt";
|
||||||
|
public const string LocalVersion = "1.5.2";
|
||||||
|
[MenuItem("ParrelSync/Check for update", priority = 20)]
|
||||||
|
static void CheckForUpdate()
|
||||||
|
{
|
||||||
|
using (System.Net.WebClient client = new System.Net.WebClient())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//This won't work with UPM packages
|
||||||
|
//string localVersionText = AssetDatabase.LoadAssetAtPath<TextAsset>(LocalVersionFilePath).text;
|
||||||
|
|
||||||
|
string localVersionText = LocalVersion;
|
||||||
|
Debug.Log("Local version text : " + LocalVersion);
|
||||||
|
|
||||||
|
string latesteVersionText = client.DownloadString(ExternalLinks.RemoteVersionURL);
|
||||||
|
Debug.Log("latest version text got: " + latesteVersionText);
|
||||||
|
string messageBody = "Current Version: " + localVersionText +"\n"
|
||||||
|
+"Latest Version: " + latesteVersionText + "\n";
|
||||||
|
var latestVersion = new Version(latesteVersionText);
|
||||||
|
var localVersion = new Version(localVersionText);
|
||||||
|
|
||||||
|
if (latestVersion > localVersion)
|
||||||
|
{
|
||||||
|
Debug.Log("There's a newer version");
|
||||||
|
messageBody += "There's a newer version available";
|
||||||
|
if(EditorUtility.DisplayDialog("Check for update.", messageBody, "Get latest release", "Close"))
|
||||||
|
{
|
||||||
|
Application.OpenURL(ExternalLinks.Releases);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Log("Current version is up-to-date.");
|
||||||
|
messageBody += "Current version is up-to-date.";
|
||||||
|
EditorUtility.DisplayDialog("Check for update.", messageBody,"OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception exp)
|
||||||
|
{
|
||||||
|
Debug.LogError("Error with checking update. Exception: " + exp);
|
||||||
|
EditorUtility.DisplayDialog("Update Error","Error with checking update. \nSee console for more details.",
|
||||||
|
"OK"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d3453b3f1a20ea148b5028f8556a7be5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,73 @@
|
||||||
|
namespace ParrelSync
|
||||||
|
{
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
[InitializeOnLoad]
|
||||||
|
public class ValidateCopiedFoldersIntegrity
|
||||||
|
{
|
||||||
|
const string SessionStateKey = "ValidateCopiedFoldersIntegrity_Init";
|
||||||
|
/// <summary>
|
||||||
|
/// Called once on editor startup.
|
||||||
|
/// Validate copied folders integrity in clone project
|
||||||
|
/// </summary>
|
||||||
|
static ValidateCopiedFoldersIntegrity()
|
||||||
|
{
|
||||||
|
if (!SessionState.GetBool(SessionStateKey, false))
|
||||||
|
{
|
||||||
|
SessionState.SetBool(SessionStateKey, true);
|
||||||
|
if (!ClonesManager.IsClone()) { return; }
|
||||||
|
|
||||||
|
ValidateFolder(ClonesManager.GetCurrentProjectPath(), ClonesManager.GetOriginalProjectPath(), "Packages");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ValidateFolder(string targetRoot, string originalRoot, string folderName)
|
||||||
|
{
|
||||||
|
var targetFolderPath = Path.Combine(targetRoot, folderName);
|
||||||
|
var targetFolderHash = CreateMd5ForFolder(targetFolderPath);
|
||||||
|
|
||||||
|
var originalFolderPath = Path.Combine(originalRoot, folderName);
|
||||||
|
var originalFolderHash = CreateMd5ForFolder(originalFolderPath);
|
||||||
|
|
||||||
|
if (targetFolderHash != originalFolderHash)
|
||||||
|
{
|
||||||
|
Debug.Log("ParrelSync: Detected changes in '" + folderName + "' directory. Updating cloned project...");
|
||||||
|
FileUtil.ReplaceDirectory(originalFolderPath, targetFolderPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static string CreateMd5ForFolder(string path)
|
||||||
|
{
|
||||||
|
// assuming you want to include nested folders
|
||||||
|
var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
|
||||||
|
.OrderBy(p => p).ToList();
|
||||||
|
|
||||||
|
MD5 md5 = MD5.Create();
|
||||||
|
|
||||||
|
for (int i = 0; i < files.Count; i++)
|
||||||
|
{
|
||||||
|
string file = files[i];
|
||||||
|
|
||||||
|
// hash path
|
||||||
|
string relativePath = file.Substring(path.Length + 1);
|
||||||
|
byte[] pathBytes = Encoding.UTF8.GetBytes(relativePath.ToLower());
|
||||||
|
md5.TransformBlock(pathBytes, 0, pathBytes.Length, pathBytes, 0);
|
||||||
|
|
||||||
|
// hash contents
|
||||||
|
byte[] contentBytes = File.ReadAllBytes(file);
|
||||||
|
if (i == files.Count - 1)
|
||||||
|
md5.TransformFinalBlock(contentBytes, 0, contentBytes.Length);
|
||||||
|
else
|
||||||
|
md5.TransformBlock(contentBytes, 0, contentBytes.Length, contentBytes, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return BitConverter.ToString(md5.Hash).Replace("-", "").ToLower();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d8fb344b9abf5274abd744833474b087
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: be3350a372dbcf64d8639b5b0bd12cd5
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,15 @@
|
||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!114 &11400000
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 0}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: c0011418c9d75434988a06b6df93b283, type: 3}
|
||||||
|
m_Name: ParrelSyncProjectSettings
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
m_OptionalSymbolicLinkFolders: []
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 305a7adc29a0ab94e9c01c35d9975a01
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"name": "com.veriorpies.parrelsync",
|
||||||
|
"displayName": "ParrelSync",
|
||||||
|
"version": "1.5.2",
|
||||||
|
"unity": "2018.4",
|
||||||
|
"description": "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.",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [ "Networking", "Utils", "Editor", "Extensions" ],
|
||||||
|
"dependencies": {}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a2a889c264e34b47a7349cbcb2cbedd7
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"name": "ParrelSync",
|
||||||
|
"references": [],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 894a6cc6ed5cd2645bb542978cbed6a9
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 637b5757dd898be4c84142abf068a4f5
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Changelog
|
||||||
|
All notable changes to this package will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Targets the Netcode for GameObjects 1.0.0 package.
|
||||||
|
- Renamed namespaces from MLAPI to Netcode.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Removed support for channels.
|
||||||
|
- No longer send 1 byte of channel information in each message.
|
||||||
|
|
||||||
|
## 1.0.0
|
||||||
|
First version of the Facepunch Transport as a Unity package.
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0a024bb1894e44e4b84f176b0b386f24
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,9 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Unity Technologies
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 56fa6d8e6a036b74eaef27818e3698c4
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Facepunch Transport for Netcode for GameObjects
|
||||||
|
|
||||||
|
By **Nico Thomas**, **Floris van Onna**<br>
|
||||||
|
Credits to **Garry Newman** (Author of Facepunch.Steamworks)
|
||||||
|
|
||||||
|
Uses Facepunch.Steamworks version 2.3.2
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8ffe30828b308c245b69db7e4a4787d8
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d8bfbe123de7f4140b6b2071b1a7cbfa
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3efa411affbe3c0499680e1e80764ef8
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,82 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 04a306ac7c3c4774ca834912812ae02a
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
: Any
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
Exclude Editor: 0
|
||||||
|
Exclude Linux64: 0
|
||||||
|
Exclude OSXUniversal: 1
|
||||||
|
Exclude WebGL: 1
|
||||||
|
Exclude Win: 1
|
||||||
|
Exclude Win64: 1
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
OS: Linux
|
||||||
|
- first:
|
||||||
|
Facebook: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Facebook: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Linux64
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: OSXUniversal
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Windows Store Apps: WindowsStoreApps
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c92697c007eab5545aae7d3c5976a5ee
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,75 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9524f64e08f2a91489798ebddb71d4e2
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
: Any
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
Exclude Editor: 0
|
||||||
|
Exclude Linux64: 1
|
||||||
|
Exclude OSXUniversal: 0
|
||||||
|
Exclude WebGL: 1
|
||||||
|
Exclude Win: 1
|
||||||
|
Exclude Win64: 1
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
OS: OSX
|
||||||
|
- first:
|
||||||
|
Standalone: Linux64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: OSXUniversal
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: x86
|
||||||
|
- first:
|
||||||
|
Standalone: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: x86_64
|
||||||
|
- first:
|
||||||
|
WebGL: WebGL
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Windows Store Apps: WindowsStoreApps
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 141e4adb324df2c41b48adf680e17ee7
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,81 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fc89a528dd38bd04a90af929e9c0f80e
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
'': Any
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
Exclude Editor: 0
|
||||||
|
Exclude Linux64: 0
|
||||||
|
Exclude OSXUniversal: 0
|
||||||
|
Exclude Win: 1
|
||||||
|
Exclude Win64: 1
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
OS: OSX
|
||||||
|
- first:
|
||||||
|
Facebook: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Facebook: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Linux64
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: OSXUniversal
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Windows Store Apps: WindowsStoreApps
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f13b7820b3a9b6145a8ea48a92291748
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,81 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fb41692bc4208c0449c96c0576331408
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
: Any
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
Exclude Editor: 0
|
||||||
|
Exclude Linux64: 1
|
||||||
|
Exclude OSXUniversal: 1
|
||||||
|
Exclude Win: 0
|
||||||
|
Exclude Win64: 1
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: x86
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
OS: Windows
|
||||||
|
- first:
|
||||||
|
Facebook: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Facebook: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Linux64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: OSXUniversal
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Win
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Windows Store Apps: WindowsStoreApps
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1c9eb7c3219a16948b7520dc7026cf20
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,95 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b3ad7ccc15f481747842885a21b7b4ab
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
: Any
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
Exclude Editor: 0
|
||||||
|
Exclude Linux: 1
|
||||||
|
Exclude Linux64: 1
|
||||||
|
Exclude LinuxUniversal: 1
|
||||||
|
Exclude OSXUniversal: 1
|
||||||
|
Exclude Win: 1
|
||||||
|
Exclude Win64: 0
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: x86_64
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
OS: Windows
|
||||||
|
- first:
|
||||||
|
Facebook: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Facebook: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Linux
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Linux64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: LinuxUniversal
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: OSXUniversal
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Win64
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Windows Store Apps: WindowsStoreApps
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ea452b431085aed499c01339e89fce8b
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9eb418beccc204946862a1a8f099ec39
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ce9561d2de976e74684ab44c5fec0813
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,89 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fd99b19e202e95a44ace17e10bac2feb
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
'': Any
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
Exclude Editor: 1
|
||||||
|
Exclude Linux: 1
|
||||||
|
Exclude Linux64: 1
|
||||||
|
Exclude LinuxUniversal: 1
|
||||||
|
Exclude OSXUniversal: 1
|
||||||
|
Exclude Win: 1
|
||||||
|
Exclude Win64: 1
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
OS: AnyOS
|
||||||
|
- first:
|
||||||
|
Facebook: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Facebook: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Linux
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Linux64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: LinuxUniversal
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: OSXUniversal
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2b478e6d3d1ef9848b43453c8e68cd0d
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,89 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a3b75fd2a03fb3149b60c2040555c3fe
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
'': Any
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
Exclude Editor: 0
|
||||||
|
Exclude Linux: 1
|
||||||
|
Exclude Linux64: 0
|
||||||
|
Exclude LinuxUniversal: 0
|
||||||
|
Exclude OSXUniversal: 1
|
||||||
|
Exclude Win: 0
|
||||||
|
Exclude Win64: 0
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: x86_64
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
OS: Linux
|
||||||
|
- first:
|
||||||
|
Facebook: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Facebook: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Linux
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Linux64
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: x86_64
|
||||||
|
- first:
|
||||||
|
Standalone: LinuxUniversal
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: x86_64
|
||||||
|
- first:
|
||||||
|
Standalone: OSXUniversal
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Win
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Win64
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 93319165ca0834f41b428adbdad19105
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,32 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7d6647fb9d80f5b4f9b2ff1378756bee
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
- first:
|
||||||
|
Standalone: OSXUniversal
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,89 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f47308500f9b7734392a75ff281c7457
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
: Any
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
Exclude Editor: 0
|
||||||
|
Exclude Linux: 0
|
||||||
|
Exclude Linux64: 0
|
||||||
|
Exclude LinuxUniversal: 0
|
||||||
|
Exclude OSXUniversal: 0
|
||||||
|
Exclude Win: 0
|
||||||
|
Exclude Win64: 1
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: x86
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
OS: Windows
|
||||||
|
- first:
|
||||||
|
Facebook: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Facebook: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Linux
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: x86
|
||||||
|
- first:
|
||||||
|
Standalone: Linux64
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: LinuxUniversal
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: OSXUniversal
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Win
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3ffd5813d91aefd459583d77d2e49ddd
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4080c4017456bde44a6f4b5915b8d27c
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,89 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cf5718c4ee1c31e458f8a58a77f4eef0
|
||||||
|
PluginImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
iconMap: {}
|
||||||
|
executionOrder: {}
|
||||||
|
defineConstraints: []
|
||||||
|
isPreloaded: 0
|
||||||
|
isOverridable: 0
|
||||||
|
isExplicitlyReferenced: 0
|
||||||
|
validateReferences: 1
|
||||||
|
platformData:
|
||||||
|
- first:
|
||||||
|
'': Any
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
Exclude Editor: 0
|
||||||
|
Exclude Linux: 0
|
||||||
|
Exclude Linux64: 0
|
||||||
|
Exclude LinuxUniversal: 0
|
||||||
|
Exclude OSXUniversal: 0
|
||||||
|
Exclude Win: 1
|
||||||
|
Exclude Win64: 0
|
||||||
|
- first:
|
||||||
|
Any:
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings: {}
|
||||||
|
- first:
|
||||||
|
Editor: Editor
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: x86_64
|
||||||
|
DefaultValueInitialized: true
|
||||||
|
OS: AnyOS
|
||||||
|
- first:
|
||||||
|
Facebook: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Facebook: Win64
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Linux
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: x86
|
||||||
|
- first:
|
||||||
|
Standalone: Linux64
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: x86_64
|
||||||
|
- first:
|
||||||
|
Standalone: LinuxUniversal
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: OSXUniversal
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
- first:
|
||||||
|
Standalone: Win
|
||||||
|
second:
|
||||||
|
enabled: 0
|
||||||
|
settings:
|
||||||
|
CPU: None
|
||||||
|
- first:
|
||||||
|
Standalone: Win64
|
||||||
|
second:
|
||||||
|
enabled: 1
|
||||||
|
settings:
|
||||||
|
CPU: AnyCPU
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b7f47a56d1502a54aac85b9fadc6741e
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,305 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Steamworks;
|
||||||
|
using Steamworks.Data;
|
||||||
|
using Unity.Netcode;
|
||||||
|
using UnityEngine;
|
||||||
|
using Unity.Collections.LowLevel.Unsafe;
|
||||||
|
|
||||||
|
namespace Netcode.Transports.Facepunch
|
||||||
|
{
|
||||||
|
using SocketConnection = Connection;
|
||||||
|
|
||||||
|
public class FacepunchTransport : NetworkTransport, IConnectionManager, ISocketManager
|
||||||
|
{
|
||||||
|
private ConnectionManager connectionManager;
|
||||||
|
private SocketManager socketManager;
|
||||||
|
private Dictionary<ulong, Client> connectedClients;
|
||||||
|
|
||||||
|
[Space]
|
||||||
|
[Tooltip("The Steam App ID of your game. Technically you're not allowed to use 480, but Valve doesn't do anything about it so it's fine for testing purposes.")]
|
||||||
|
[SerializeField] private uint steamAppId = 480;
|
||||||
|
|
||||||
|
[Tooltip("The Steam ID of the user targeted when joining as a client.")]
|
||||||
|
[SerializeField] public ulong targetSteamId;
|
||||||
|
|
||||||
|
[Header("Info")]
|
||||||
|
[ReadOnly]
|
||||||
|
[Tooltip("When in play mode, this will display your Steam ID.")]
|
||||||
|
[SerializeField] private ulong userSteamId;
|
||||||
|
|
||||||
|
private LogLevel LogLevel => NetworkManager.Singleton.LogLevel;
|
||||||
|
|
||||||
|
private class Client
|
||||||
|
{
|
||||||
|
public SteamId steamId;
|
||||||
|
public SocketConnection connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region MonoBehaviour Messages
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SteamClient.Init(steamAppId, false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (LogLevel <= LogLevel.Error)
|
||||||
|
Debug.LogError($"[{nameof(FacepunchTransport)}] - Caught an exeption during initialization of Steam client: {e}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
StartCoroutine(InitSteamworks());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
SteamClient.RunCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
SteamClient.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region NetworkTransport Overrides
|
||||||
|
|
||||||
|
public override ulong ServerClientId => 0;
|
||||||
|
|
||||||
|
public override void DisconnectLocalClient()
|
||||||
|
{
|
||||||
|
connectionManager?.Connection.Close();
|
||||||
|
|
||||||
|
if (LogLevel <= LogLevel.Developer)
|
||||||
|
Debug.Log($"[{nameof(FacepunchTransport)}] - Disconnecting local client.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DisconnectRemoteClient(ulong clientId)
|
||||||
|
{
|
||||||
|
if (connectedClients.TryGetValue(clientId, out Client user))
|
||||||
|
{
|
||||||
|
// Flush any pending messages before closing the connection
|
||||||
|
user.connection.Flush();
|
||||||
|
user.connection.Close();
|
||||||
|
connectedClients.Remove(clientId);
|
||||||
|
|
||||||
|
if (LogLevel <= LogLevel.Developer)
|
||||||
|
Debug.Log($"[{nameof(FacepunchTransport)}] - Disconnecting remote client with ID {clientId}.");
|
||||||
|
}
|
||||||
|
else if (LogLevel <= LogLevel.Normal)
|
||||||
|
Debug.LogWarning($"[{nameof(FacepunchTransport)}] - Failed to disconnect remote client with ID {clientId}, client not connected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override unsafe ulong GetCurrentRtt(ulong clientId)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize(NetworkManager networkManager = null)
|
||||||
|
{
|
||||||
|
connectedClients = new Dictionary<ulong, Client>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SendType NetworkDeliveryToSendType(NetworkDelivery delivery)
|
||||||
|
{
|
||||||
|
return delivery switch
|
||||||
|
{
|
||||||
|
NetworkDelivery.Reliable => SendType.Reliable,
|
||||||
|
NetworkDelivery.ReliableFragmentedSequenced => SendType.Reliable,
|
||||||
|
NetworkDelivery.ReliableSequenced => SendType.Reliable,
|
||||||
|
NetworkDelivery.Unreliable => SendType.Unreliable,
|
||||||
|
NetworkDelivery.UnreliableSequenced => SendType.Unreliable,
|
||||||
|
_ => SendType.Reliable
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Shutdown()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (LogLevel <= LogLevel.Developer)
|
||||||
|
Debug.Log($"[{nameof(FacepunchTransport)}] - Shutting down.");
|
||||||
|
|
||||||
|
connectionManager?.Close();
|
||||||
|
socketManager?.Close();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (LogLevel <= LogLevel.Error)
|
||||||
|
Debug.LogError($"[{nameof(FacepunchTransport)}] - Caught an exception while shutting down: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Send(ulong clientId, ArraySegment<byte> data, NetworkDelivery delivery)
|
||||||
|
{
|
||||||
|
var sendType = NetworkDeliveryToSendType(delivery);
|
||||||
|
|
||||||
|
if (clientId == ServerClientId)
|
||||||
|
connectionManager.Connection.SendMessage(data.Array, data.Offset, data.Count, sendType);
|
||||||
|
else if (connectedClients.TryGetValue(clientId, out Client user))
|
||||||
|
user.connection.SendMessage(data.Array, data.Offset, data.Count, sendType);
|
||||||
|
else if (LogLevel <= LogLevel.Normal)
|
||||||
|
Debug.LogWarning($"[{nameof(FacepunchTransport)}] - Failed to send packet to remote client with ID {clientId}, client not connected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override NetworkEvent PollEvent(out ulong clientId, out ArraySegment<byte> payload, out float receiveTime)
|
||||||
|
{
|
||||||
|
connectionManager?.Receive();
|
||||||
|
socketManager?.Receive();
|
||||||
|
|
||||||
|
clientId = 0;
|
||||||
|
receiveTime = Time.realtimeSinceStartup;
|
||||||
|
payload = default;
|
||||||
|
return NetworkEvent.Nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool StartClient()
|
||||||
|
{
|
||||||
|
if (LogLevel <= LogLevel.Developer)
|
||||||
|
Debug.Log($"[{nameof(FacepunchTransport)}] - Starting as client.");
|
||||||
|
|
||||||
|
connectionManager = SteamNetworkingSockets.ConnectRelay<ConnectionManager>(targetSteamId);
|
||||||
|
connectionManager.Interface = this;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool StartServer()
|
||||||
|
{
|
||||||
|
if (LogLevel <= LogLevel.Developer)
|
||||||
|
Debug.Log($"[{nameof(FacepunchTransport)}] - Starting as server.");
|
||||||
|
|
||||||
|
socketManager = SteamNetworkingSockets.CreateRelaySocket<SocketManager>();
|
||||||
|
socketManager.Interface = this;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ConnectionManager Implementation
|
||||||
|
|
||||||
|
private byte[] payloadCache = new byte[4096];
|
||||||
|
|
||||||
|
private void EnsurePayloadCapacity(int size)
|
||||||
|
{
|
||||||
|
if (payloadCache.Length >= size)
|
||||||
|
return;
|
||||||
|
|
||||||
|
payloadCache = new byte[Math.Max(payloadCache.Length * 2, size)];
|
||||||
|
}
|
||||||
|
|
||||||
|
void IConnectionManager.OnConnecting(ConnectionInfo info)
|
||||||
|
{
|
||||||
|
if (LogLevel <= LogLevel.Developer)
|
||||||
|
Debug.Log($"[{nameof(FacepunchTransport)}] - Connecting with Steam user {info.Identity.SteamId}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void IConnectionManager.OnConnected(ConnectionInfo info)
|
||||||
|
{
|
||||||
|
InvokeOnTransportEvent(NetworkEvent.Connect, ServerClientId, default, Time.realtimeSinceStartup);
|
||||||
|
|
||||||
|
if (LogLevel <= LogLevel.Developer)
|
||||||
|
Debug.Log($"[{nameof(FacepunchTransport)}] - Connected with Steam user {info.Identity.SteamId}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void IConnectionManager.OnDisconnected(ConnectionInfo info)
|
||||||
|
{
|
||||||
|
InvokeOnTransportEvent(NetworkEvent.Disconnect, ServerClientId, default, Time.realtimeSinceStartup);
|
||||||
|
|
||||||
|
if (LogLevel <= LogLevel.Developer)
|
||||||
|
Debug.Log($"[{nameof(FacepunchTransport)}] - Disconnected Steam user {info.Identity.SteamId}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe void IConnectionManager.OnMessage(IntPtr data, int size, long messageNum, long recvTime, int channel)
|
||||||
|
{
|
||||||
|
EnsurePayloadCapacity(size);
|
||||||
|
|
||||||
|
fixed (byte* payload = payloadCache)
|
||||||
|
{
|
||||||
|
UnsafeUtility.MemCpy(payload, (byte*)data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
InvokeOnTransportEvent(NetworkEvent.Data, ServerClientId, new ArraySegment<byte>(payloadCache, 0, size), Time.realtimeSinceStartup);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region SocketManager Implementation
|
||||||
|
|
||||||
|
void ISocketManager.OnConnecting(SocketConnection connection, ConnectionInfo info)
|
||||||
|
{
|
||||||
|
if (LogLevel <= LogLevel.Developer)
|
||||||
|
Debug.Log($"[{nameof(FacepunchTransport)}] - Accepting connection from Steam user {info.Identity.SteamId}.");
|
||||||
|
|
||||||
|
connection.Accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ISocketManager.OnConnected(SocketConnection connection, ConnectionInfo info)
|
||||||
|
{
|
||||||
|
if (!connectedClients.ContainsKey(connection.Id))
|
||||||
|
{
|
||||||
|
connectedClients.Add(connection.Id, new Client()
|
||||||
|
{
|
||||||
|
connection = connection,
|
||||||
|
steamId = info.Identity.SteamId
|
||||||
|
});
|
||||||
|
|
||||||
|
InvokeOnTransportEvent(NetworkEvent.Connect, connection.Id, default, Time.realtimeSinceStartup);
|
||||||
|
|
||||||
|
if (LogLevel <= LogLevel.Developer)
|
||||||
|
Debug.Log($"[{nameof(FacepunchTransport)}] - Connected with Steam user {info.Identity.SteamId}.");
|
||||||
|
}
|
||||||
|
else if (LogLevel <= LogLevel.Normal)
|
||||||
|
Debug.LogWarning($"[{nameof(FacepunchTransport)}] - Failed to connect client with ID {connection.Id}, client already connected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ISocketManager.OnDisconnected(SocketConnection connection, ConnectionInfo info)
|
||||||
|
{
|
||||||
|
connectedClients.Remove(connection.Id);
|
||||||
|
|
||||||
|
InvokeOnTransportEvent(NetworkEvent.Disconnect, connection.Id, default, Time.realtimeSinceStartup);
|
||||||
|
|
||||||
|
if (LogLevel <= LogLevel.Developer)
|
||||||
|
Debug.Log($"[{nameof(FacepunchTransport)}] - Disconnected Steam user {info.Identity.SteamId}");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe void ISocketManager.OnMessage(SocketConnection connection, NetIdentity identity, IntPtr data, int size, long messageNum, long recvTime, int channel)
|
||||||
|
{
|
||||||
|
EnsurePayloadCapacity(size);
|
||||||
|
|
||||||
|
fixed (byte* payload = payloadCache)
|
||||||
|
{
|
||||||
|
UnsafeUtility.MemCpy(payload, (byte*)data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
InvokeOnTransportEvent(NetworkEvent.Data, connection.Id, new ArraySegment<byte>(payloadCache, 0, size), Time.realtimeSinceStartup);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Utility Methods
|
||||||
|
|
||||||
|
private IEnumerator InitSteamworks()
|
||||||
|
{
|
||||||
|
yield return new WaitUntil(() => SteamClient.IsValid);
|
||||||
|
|
||||||
|
SteamNetworkingUtils.InitRelayNetworkAccess();
|
||||||
|
|
||||||
|
if (LogLevel <= LogLevel.Developer)
|
||||||
|
Debug.Log($"[{nameof(FacepunchTransport)}] - Initialized access to Steam Relay Network.");
|
||||||
|
|
||||||
|
userSteamId = SteamClient.SteamId;
|
||||||
|
|
||||||
|
if (LogLevel <= LogLevel.Developer)
|
||||||
|
Debug.Log($"[{nameof(FacepunchTransport)}] - Fetched user Steam ID.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 44dd247a00edbfa44b77868d755af6ea
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,23 @@
|
||||||
|
using UnityEngine;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Netcode.Transports.Facepunch
|
||||||
|
{
|
||||||
|
public class ReadOnlyAttribute : PropertyAttribute { }
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
|
||||||
|
public class ReadOnlyDrawer : PropertyDrawer
|
||||||
|
{
|
||||||
|
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||||
|
{
|
||||||
|
var previousGUIState = GUI.enabled;
|
||||||
|
GUI.enabled = false;
|
||||||
|
EditorGUI.PropertyField(position, property, label);
|
||||||
|
GUI.enabled = previousGUIState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 43f0baab69c269949b096603ef4f7cf6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "Facepunch Transport for Netcode for GameObjects",
|
||||||
|
"rootNamespace": "Netcode.Transports.Facepunch",
|
||||||
|
"references": [
|
||||||
|
"Unity.Netcode.Runtime"
|
||||||
|
],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor",
|
||||||
|
"LinuxStandalone64",
|
||||||
|
"macOSStandalone",
|
||||||
|
"WindowsStandalone32",
|
||||||
|
"WindowsStandalone64"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": true,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2aa43b0c797e0d444a7e82e75d4f2082
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "com.community.netcode.transport.facepunch",
|
||||||
|
"displayName": "Facepunch Transport for Netcode for GameObjects",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"unity": "2019.4",
|
||||||
|
"description": "Facepunch Transport for Netcode for GameObjects",
|
||||||
|
"author": "Nico Thomas (Transport Adapter), Floris van Onna (Transport Adapter), Garry Newman (Author of Facepunch.Steamworks)",
|
||||||
|
"dependencies": {
|
||||||
|
"com.unity.netcode.gameobjects": "1.0.0-pre.4"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f719a183491e7c849a3308ea58b10205
|
||||||
|
PackageManifestImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue