253 lines
7.5 KiB
C#
253 lines
7.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using JetBrains.Annotations;
|
|
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.SceneManagement;
|
|
using Needle.Engine.Utils;
|
|
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
namespace Needle.Engine
|
|
{
|
|
[HelpURL(Constants.DocumentationUrl)]
|
|
[AddComponentMenu("Needle Engine/Needle Engine (Project & Export)" + Constants.NeedleComponentTags +
|
|
" WebProject WebExport threejs webgl")]
|
|
[NeedleEngineIgnore]
|
|
public class ExportInfo : MonoBehaviour, IProjectInfo
|
|
{
|
|
/**
|
|
* The last target directory that was used for exporting.
|
|
*/
|
|
public static string LastExportDirectory
|
|
{
|
|
#if UNITY_EDITOR
|
|
get => SessionState.GetString("NeedleEngine.LastExportDirectory", "");
|
|
set => SessionState.SetString("NeedleEngine.LastExportDirectory", value);
|
|
#else
|
|
get => "";
|
|
set { }
|
|
#endif
|
|
}
|
|
|
|
[CanBeNull]
|
|
public static ExportInfo Get(bool findInactive = false)
|
|
{
|
|
// UNITY_2020_3_45_OR_NEWER || UNITY_2022_2_5_OR_NEWER || UNITY_2021_3_18_OR_NEWER
|
|
#if UNITY_2023_1_OR_NEWER
|
|
return FindAnyObjectByType<ExportInfo>(findInactive ? FindObjectsInactive.Include : FindObjectsInactive.Exclude);
|
|
#else
|
|
return FindObjectOfType<ExportInfo>(findInactive);
|
|
#endif
|
|
}
|
|
|
|
internal static event Action<IProjectInfo> RequestGeneratingTempProject;
|
|
|
|
string IProjectInfo.ProjectDirectory => GetProjectDirectory();
|
|
string IProjectInfo.AssetsDirectory => GetProjectDirectory() + "/assets";
|
|
string IProjectInfo.BaseUrl => NeedleProjectConfig.TryGetBaseUrl(out var baseUrl) ? baseUrl : null;
|
|
|
|
public static string GetFullPath(string directoryName) =>
|
|
Path.GetFullPath(Application.dataPath + "/../" + directoryName);
|
|
|
|
[Tooltip(
|
|
"The relative path to your javascript project where the Unity scene will be exported to. To create a new project just enter a new path, select a template from and click \"Generate project\"")]
|
|
public string DirectoryName = null;
|
|
|
|
/// <summary>
|
|
/// A web project can be cloned from a remote repository. If this is set and the local directory doesnt exist this will be used to clone the project
|
|
/// </summary>
|
|
public string RemoteUrl = null;
|
|
|
|
[Tooltip("Exporting on save when enabled, otherwise only via menu items or build")]
|
|
public bool AutoExport = true;
|
|
|
|
[Tooltip("Apply Compression automatically on export when enabled")]
|
|
public bool AutoCompress = true;
|
|
|
|
[SerializeField] public List<Dependency> Dependencies = new List<Dependency>();
|
|
|
|
public string CloudName;
|
|
public string CloudTeam;
|
|
|
|
public string BasePath => _basePath ??= Application.dataPath + "/../";
|
|
|
|
public string GetProjectDirectory()
|
|
{
|
|
if (DirectoryName.StartsWith("Assets/")) return Path.GetFullPath(DirectoryName);
|
|
if (DirectoryName.StartsWith("Packages/")) return Path.GetFullPath(DirectoryName);
|
|
var subPath = DirectoryName;
|
|
if (string.IsNullOrWhiteSpace(subPath)) subPath = "Library/Needle";
|
|
return BasePath + subPath;
|
|
}
|
|
|
|
public bool Exists() => !string.IsNullOrWhiteSpace(DirectoryName) && File.Exists(PackageJsonPath);
|
|
|
|
public string PackageJsonPath => DirectoryName + "/package.json";
|
|
|
|
public bool IsInstalled()
|
|
{
|
|
if (!Exists()) return false;
|
|
var dir = GetProjectDirectory() + "/node_modules/" + Constants.RuntimeNpmPackageName;
|
|
return !string.IsNullOrEmpty(dir) && Directory.Exists(dir);
|
|
}
|
|
|
|
/// <summary>
|
|
/// a project is considered a temp project when it is in Library/
|
|
/// </summary>
|
|
public bool IsTempProject()
|
|
{
|
|
var dir = DirectoryName;
|
|
if (dir == null) return false;
|
|
if (dir.StartsWith("Library/")) return true;
|
|
return false;
|
|
}
|
|
|
|
public bool IsValidDirectory() => IsValidDirectory(DirectoryName, out _);
|
|
|
|
public static bool IsValidDirectory(string dir, out string reason)
|
|
{
|
|
reason = null;
|
|
if (string.IsNullOrWhiteSpace(dir))
|
|
{
|
|
reason = "Path is empty";
|
|
return false;
|
|
}
|
|
if (dir.StartsWith("Temp/"))
|
|
{
|
|
reason = "Path is in Temp folder";
|
|
return false;
|
|
}
|
|
var isAssetsPath = dir.StartsWith("Assets/");
|
|
var isPackagesPath = dir.StartsWith("Packages/");
|
|
if (isAssetsPath || isPackagesPath)
|
|
{
|
|
// web projects may exist in the Assets or Packages folder if they're hidden (so Unity wont import them)
|
|
// TODO: this doesnt work if a parent folder of the Unity project contains a ~
|
|
var isHidden = dir.EndsWith("~") || dir.Contains("~/") || dir.Contains("~\\");
|
|
if (!isHidden)
|
|
reason =
|
|
"Path is in Assets or Packages folder but is not hidden (e.g. a folder in the path must contain '~')\n" +
|
|
"Valid examples:\n" +
|
|
"• Assets/MyProject~ (inside a hidden folder in Assets)\n" +
|
|
"• Needle/MyProject (next to the Unity project)\n" +
|
|
"• Library/MyProject (a temporary project)";
|
|
return isHidden;
|
|
}
|
|
try
|
|
{
|
|
if (Path.IsPathRooted(dir))
|
|
{
|
|
reason = "Path is absolute";
|
|
return false;
|
|
}
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
reason = "Path must not end with a slash or backslash";
|
|
return !(dir.EndsWith("\\") || dir.EndsWith("/"));
|
|
}
|
|
|
|
private string _packageJsonPath;
|
|
private static string _basePath;
|
|
|
|
private void OnValidate()
|
|
{
|
|
_packageJsonPath = DirectoryName + "/package.json";
|
|
|
|
if (DirectoryName == null || DirectoryName.Length <= 0)
|
|
{
|
|
if(!string.IsNullOrWhiteSpace(LastExportDirectory))
|
|
DirectoryName = LastExportDirectory.RelativeTo(BasePath);
|
|
else
|
|
CreateName(SceneManager.GetActiveScene());
|
|
|
|
if (transform.childCount <= 0 && GetComponents<Component>().Length <= 2)
|
|
{
|
|
SetNameAndTag();
|
|
}
|
|
|
|
async void SetNameAndTag()
|
|
{
|
|
await Task.Yield();
|
|
gameObject.name = "Export Info";
|
|
// ReSharper disable once Unity.InefficientPropertyAccess
|
|
gameObject.tag = "EditorOnly";
|
|
}
|
|
}
|
|
|
|
if (DirectoryName != null && (DirectoryName.Contains("github.com") ||
|
|
DirectoryName.Contains("gitlab.com") || DirectoryName.EndsWith(".git")))
|
|
{
|
|
RemoteUrl = DirectoryName;
|
|
}
|
|
|
|
// Do not implicitly remove dependencies here, we want to show these in the inspector
|
|
// It may happen if for example an npmdef is copied outside of unity but the hidden package folder is still there
|
|
// for (var index = Dependencies.Count - 1; index >= 0; index--)
|
|
// {
|
|
// var dep = Dependencies[index];
|
|
// if (string.IsNullOrEmpty(dep.Name))
|
|
// {
|
|
// Dependencies.RemoveAt(index);
|
|
// }
|
|
// }
|
|
|
|
if (IsValidDirectory() && string.IsNullOrWhiteSpace(CloudName))
|
|
{
|
|
var activeScene = SceneManager.GetActiveScene();
|
|
if (activeScene.IsValid())
|
|
CloudName = activeScene.name;
|
|
}
|
|
}
|
|
|
|
internal void RequestInstallationIfTempProjectAndNotExists()
|
|
{
|
|
var dir = DirectoryName;
|
|
if (!string.IsNullOrEmpty(dir) && IsValidDirectory())
|
|
{
|
|
if (!IsInstalled() || !Directory.Exists(dir))
|
|
{
|
|
if (IsTempProject())
|
|
{
|
|
RequestGeneratingTempProject?.Invoke(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void CreateName(Scene scene)
|
|
{
|
|
var projectName = scene.name;
|
|
if (string.IsNullOrWhiteSpace(projectName)) projectName = "newProject";
|
|
DirectoryName = "Needle/" + projectName;
|
|
}
|
|
|
|
#if UNITY_EDITOR_WIN
|
|
[ContextMenu("Open in commandline")]
|
|
private void OpenCmdLine()
|
|
{
|
|
var dir = GetProjectDirectory();
|
|
if (!Exists()) Debug.LogWarning("Project directory does not exist: " + dir);
|
|
else
|
|
ProcessUtils.OpenCommandLine(GetProjectDirectory());
|
|
}
|
|
|
|
[ContextMenu("Internal/Create Web Project Config")]
|
|
private void CreateProjectConfig()
|
|
{
|
|
if (NeedleProjectConfig.TryCreate(this, out _, out var path))
|
|
{
|
|
EditorUtility.RevealInFinder(path);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
} |