diff --git a/Assets/External/Models/SidekickCharacters/Scripts/Editor/ModularCharacterWindow.cs b/Assets/External/Models/SidekickCharacters/Scripts/Editor/ModularCharacterWindow.cs
index 626ed7a7..41785c2a 100644
--- a/Assets/External/Models/SidekickCharacters/Scripts/Editor/ModularCharacterWindow.cs
+++ b/Assets/External/Models/SidekickCharacters/Scripts/Editor/ModularCharacterWindow.cs
@@ -40,7 +40,7 @@ namespace Synty.SidekickCharacters
private const string _AUTOSAVE_MISSING_PARTS = "SK_Autosave_missing_parts";
private const string _AUTOSAVE_STATE = "SK_Autosave_state";
private const string _BASE_COLOR_SET_NAME = "Species";
- private const string _BASE_COLOR_SET_PATH = "Assets/Synty/SidekickCharacters/Resources/Species";
+ private const string _BASE_COLOR_SET_PATH = "Assets/External/Models/SidekickCharacters/Resources/Species";
private const string _BASE_MESH_NAME = "Meshes/SK_BaseModel";
private const string _BASE_MATERIAL_NAME = "Materials/M_BaseMaterial";
private const string _BLEND_GENDER_NAME = "masculineFeminine";
@@ -121,7 +121,7 @@ namespace Synty.SidekickCharacters
private bool _showAllColourProperties = false;
private float _bodyTypeBlendValue;
private bool _loadingCharacter = false;
- private bool _loadingContent = true;
+ private bool _loadingContent = false;
private Image _loadingImage;
private ObjectField _materialField;
private float _musclesBlendValue;
@@ -192,7 +192,7 @@ namespace Synty.SidekickCharacters
}
// ensures we release the file lock on the database
- _dbManager.CloseConnection();
+ _dbManager?.CloseConnection();
}
#if UNITY_EDITOR
@@ -211,7 +211,7 @@ namespace Synty.SidekickCharacters
///
private void Update()
{
- if (_loadingContent)
+ if (_loadingContent && _loadingImage != null)
{
Vector3 rotation = _loadingImage.transform.rotation.eulerAngles;
rotation += new Vector3(0, 0, 0.5f * Time.deltaTime);
@@ -473,6 +473,7 @@ namespace Synty.SidekickCharacters
// if we still can't connect, something's gone wrong, don't keep building the GUI
if (_dbManager?.GetCurrentDbConnection() == null)
{
+ _loadingContent = false;
return;
}
@@ -1814,7 +1815,12 @@ namespace Synty.SidekickCharacters
documentationButton.clickable.clicked += delegate
{
- Application.OpenURL("file:Assets/Synty/SidekickCharacters/Documentation/SidekickCharacters_UserGuide.pdf");
+ string documentationPath = Path.Combine(DatabaseManager.GetPackageRootAbsolutePath() ?? string.Empty, "Documentation", "SidekickCharacters_UserGuide.pdf");
+ documentationPath = Path.GetFullPath(documentationPath);
+ if (File.Exists(documentationPath))
+ {
+ Application.OpenURL("file:" + documentationPath);
+ }
};
Button storeButton = new Button
@@ -1919,7 +1925,8 @@ namespace Synty.SidekickCharacters
///
private void SaveColorSet()
{
- string path = Path.Combine(_BASE_COLOR_SET_PATH, _currentSpecies.Name);
+ string baseColorSetPath = DatabaseManager.GetPackageAssetPath("Resources", "Species") ?? _BASE_COLOR_SET_PATH;
+ string path = Path.Combine(baseColorSetPath, _currentSpecies.Name);
path = Path.Combine(path, _currentColorSet.Name.Replace(" ", "_"));
SaveTexturesToDisk(path);
diff --git a/Assets/External/Models/SidekickCharacters/Scripts/Editor/Utility/ToolDownloader.cs b/Assets/External/Models/SidekickCharacters/Scripts/Editor/Utility/ToolDownloader.cs
index 93a04d17..c162b780 100644
--- a/Assets/External/Models/SidekickCharacters/Scripts/Editor/Utility/ToolDownloader.cs
+++ b/Assets/External/Models/SidekickCharacters/Scripts/Editor/Utility/ToolDownloader.cs
@@ -24,7 +24,7 @@ namespace Synty.SidekickCharacters
private const string _GIT_DB_URL = "https://github.com/SyntyStudios/SidekicksToolRelease/releases/latest/download/SidekicksDatabase.unitypackage";
private const string _PACKAGE_CACHE = "Assets/DownloadCache/Sidekicks.unitypackage";
private const string _DB_PACKAGE_CACHE = "Assets/DownloadCache/SidekicksDatabase.unitypackage";
- private const string _VERSION_FILE = "Assets/Synty/SidekickCharacters/Scripts/Editor/version.txt";
+ private const string _VERSION_FILE_NAME = "version.txt";
private const string _VERSION_TAG = "\"tag_name\":";
private const string _VERSION_KEY = "sk_current_tool_version";
private const string _SIDEKICK_TOOL_MENU_ITEM = "Synty/Sidekick Character Tool";
@@ -168,19 +168,61 @@ namespace Synty.SidekickCharacters
private void SaveCurrentInstalledVersion(string version)
{
- File.WriteAllText(_VERSION_FILE, version);
+ string versionFilePath = GetVersionFilePath();
+ if (string.IsNullOrEmpty(versionFilePath))
+ {
+ Debug.LogWarning("Unable to resolve Sidekick version file path.");
+ return;
+ }
+
+ File.WriteAllText(versionFilePath, version);
}
private string LoadCurrentInstalledVersion()
{
- if (File.Exists(_VERSION_FILE))
+ string versionFilePath = GetVersionFilePath();
+ if (string.IsNullOrEmpty(versionFilePath))
{
- return File.ReadAllText(_VERSION_FILE);
+ return null;
+ }
+
+ if (File.Exists(versionFilePath))
+ {
+ return File.ReadAllText(versionFilePath);
}
return null;
}
+ private string GetVersionFilePath()
+ {
+ MonoScript currentScript = MonoScript.FromScriptableObject(this);
+ if (currentScript == null)
+ {
+ return null;
+ }
+
+ string scriptPath = AssetDatabase.GetAssetPath(currentScript);
+ if (string.IsNullOrEmpty(scriptPath))
+ {
+ return null;
+ }
+
+ string utilityDirectory = Path.GetDirectoryName(scriptPath);
+ if (string.IsNullOrEmpty(utilityDirectory))
+ {
+ return null;
+ }
+
+ string editorDirectory = Path.GetDirectoryName(utilityDirectory);
+ if (string.IsNullOrEmpty(editorDirectory))
+ {
+ return null;
+ }
+
+ return Path.GetFullPath(Path.Combine(editorDirectory, _VERSION_FILE_NAME));
+ }
+
private void DownloadLatestDBVersion()
{
WebClient client = new WebClient();
diff --git a/Assets/External/Models/SidekickCharacters/Scripts/Runtime/API/SidekickRuntime.cs b/Assets/External/Models/SidekickCharacters/Scripts/Runtime/API/SidekickRuntime.cs
index 52b15710..d38b4db0 100644
--- a/Assets/External/Models/SidekickCharacters/Scripts/Runtime/API/SidekickRuntime.cs
+++ b/Assets/External/Models/SidekickCharacters/Scripts/Runtime/API/SidekickRuntime.cs
@@ -230,6 +230,42 @@ namespace Synty.SidekickCharacters.API
await runtime.PopulatePresetLibrary();
}
+ private static List GetSidekickPartFiles()
+ {
+ string packageRootPath = DatabaseManager.GetPackageRootAbsolutePath();
+ if (!string.IsNullOrEmpty(packageRootPath))
+ {
+ string meshesRootPath = Path.Combine(packageRootPath, "Resources", "Meshes");
+ if (Directory.Exists(meshesRootPath))
+ {
+ return Directory
+ .GetFiles(meshesRootPath, "SK_*_*_*_*_*.fbx", SearchOption.AllDirectories)
+ .Select(ToAssetPath)
+ .ToList();
+ }
+ }
+
+ return Directory.GetFiles("Assets", "SK_*_*_*_*_*.fbx", SearchOption.AllDirectories).ToList();
+ }
+
+ private static string ToAssetPath(string fullPath)
+ {
+ if (string.IsNullOrWhiteSpace(fullPath))
+ {
+ return fullPath;
+ }
+
+ string normalizedFullPath = fullPath.Replace('\\', '/');
+ string normalizedAssetsPath = Application.dataPath.Replace('\\', '/');
+
+ if (!normalizedFullPath.StartsWith(normalizedAssetsPath, StringComparison.OrdinalIgnoreCase))
+ {
+ return normalizedFullPath;
+ }
+
+ return "Assets" + normalizedFullPath.Substring(normalizedAssetsPath.Length);
+ }
+
///
/// Takes all the parts selected in the window, and combines them into a single model in the scene.
///
@@ -512,7 +548,7 @@ namespace Synty.SidekickCharacters.API
_partOutfitToggleMap = new Dictionary();
_partCount = 0;
- List files = Directory.GetFiles("Assets", "SK_*_*_*_*_*.fbx", SearchOption.AllDirectories).ToList();
+ List files = GetSidekickPartFiles();
foreach (CharacterPartType partType in Enum.GetValues(typeof(CharacterPartType)))
{
@@ -610,7 +646,7 @@ namespace Synty.SidekickCharacters.API
_speciesDictionary = new Dictionary();
_partCount = 0;
- List files = Directory.GetFiles("Assets", "SK_*_*_*_*_*.fbx", SearchOption.AllDirectories).ToList();
+ List files = GetSidekickPartFiles();
Dictionary filesOnDisk = new Dictionary();
foreach (string file in files)
{
diff --git a/Assets/External/Models/SidekickCharacters/Scripts/Runtime/Database/DTO/SidekickColorSet.cs b/Assets/External/Models/SidekickCharacters/Scripts/Runtime/Database/DTO/SidekickColorSet.cs
index 696956ae..02461545 100644
--- a/Assets/External/Models/SidekickCharacters/Scripts/Runtime/Database/DTO/SidekickColorSet.cs
+++ b/Assets/External/Models/SidekickCharacters/Scripts/Runtime/Database/DTO/SidekickColorSet.cs
@@ -154,6 +154,18 @@ namespace Synty.SidekickCharacters.Database.DTO
/// A color set with all DTO class properties set
private static void Decorate(DatabaseManager dbManager, SidekickColorSet set)
{
+ if (set == null)
+ {
+ return;
+ }
+
+ set.SourceColorPath = DatabaseManager.NormalizeLegacyAssetPath(set.SourceColorPath);
+ set.SourceMetallicPath = DatabaseManager.NormalizeLegacyAssetPath(set.SourceMetallicPath);
+ set.SourceSmoothnessPath = DatabaseManager.NormalizeLegacyAssetPath(set.SourceSmoothnessPath);
+ set.SourceReflectionPath = DatabaseManager.NormalizeLegacyAssetPath(set.SourceReflectionPath);
+ set.SourceEmissionPath = DatabaseManager.NormalizeLegacyAssetPath(set.SourceEmissionPath);
+ set.SourceOpacityPath = DatabaseManager.NormalizeLegacyAssetPath(set.SourceOpacityPath);
+
if (set.Species == null && set.PtrSpecies >= 0)
{
set.Species = SidekickSpecies.GetByID(dbManager, set.PtrSpecies);
diff --git a/Assets/External/Models/SidekickCharacters/Scripts/Runtime/Database/DTO/SidekickPart.cs b/Assets/External/Models/SidekickCharacters/Scripts/Runtime/Database/DTO/SidekickPart.cs
index fd2fdf2b..d0ad6246 100644
--- a/Assets/External/Models/SidekickCharacters/Scripts/Runtime/Database/DTO/SidekickPart.cs
+++ b/Assets/External/Models/SidekickCharacters/Scripts/Runtime/Database/DTO/SidekickPart.cs
@@ -222,6 +222,8 @@ namespace Synty.SidekickCharacters.Database.DTO
{
if (part != null)
{
+ part.Location = DatabaseManager.NormalizeLegacyAssetPath(part.Location);
+
if (part.Species == null && part.PtrSpecies >= 0)
{
part.Species = SidekickSpecies.GetByID(dbManager, part.PtrSpecies);
diff --git a/Assets/External/Models/SidekickCharacters/Scripts/Runtime/Database/DatabaseManager.cs b/Assets/External/Models/SidekickCharacters/Scripts/Runtime/Database/DatabaseManager.cs
index 80378c09..36ae79e6 100644
--- a/Assets/External/Models/SidekickCharacters/Scripts/Runtime/Database/DatabaseManager.cs
+++ b/Assets/External/Models/SidekickCharacters/Scripts/Runtime/Database/DatabaseManager.cs
@@ -12,7 +12,9 @@
using SQLite;
using Synty.SidekickCharacters.Database.DTO;
using System;
+using System.IO;
using System.Linq;
+using UnityEngine;
namespace Synty.SidekickCharacters.Database
{
@@ -21,11 +23,18 @@ namespace Synty.SidekickCharacters.Database
///
public class DatabaseManager
{
- private static readonly string _DATABASE_PATH = "Assets/Synty/SidekickCharacters/Database/Side_Kick_Data.db";
+ private const string _DATABASE_FILE_NAME = "Side_Kick_Data.db";
+ private const string _LEGACY_PACKAGE_ROOT = "Assets/Synty/SidekickCharacters";
+ private const string _LEGACY_TOOLS_PACKAGE_ROOT = "Assets/Synty/Tools/SidekickCharacters";
+ private const string _WORKING_DATABASE_DIRECTORY = "SidekickCharacters";
private readonly string _CURRENT_VERSION = "1.0.2";
private static SQLiteConnection _connection;
private static int _connectionHash;
+ private static string _databasePath;
+ private static string _seedDatabasePath;
+ private static string _packageRootAbsolutePath;
+ private static string _packageRootAssetPath;
///
/// Gets the DB connection with the given connection details.
@@ -39,7 +48,13 @@ namespace Synty.SidekickCharacters.Database
{
if (_connection == null)
{
- _connection = new SQLiteConnection(_DATABASE_PATH, true);
+ string databasePath = GetDatabasePath();
+ if (string.IsNullOrEmpty(databasePath))
+ {
+ throw new FileNotFoundException("Unable to locate Sidekick database file in this Unity project.");
+ }
+
+ _connection = new SQLiteConnection(databasePath, true);
}
else
{
@@ -100,12 +115,12 @@ namespace Synty.SidekickCharacters.Database
{
Species = new SidekickSpecies { ID = -1, Name = "None" },
Name = "Default",
- SourceColorPath = "Assets/Synty/Tools/SidekickCharacters/Resources/Textures/T_ColorMap.png",
- SourceMetallicPath = "Assets/Synty/Tools/SidekickCharacters/Resources/Textures/T_MetallicMap.png",
- SourceSmoothnessPath = "Assets/Synty/Tools/SidekickCharacters/Resources/Textures/T_SmoothnessMap.png",
- SourceReflectionPath = "Assets/Synty/Tools/SidekickCharacters/Resources/Textures/T_ReflectionMap.png",
- SourceEmissionPath = "Assets/Synty/Tools/SidekickCharacters/Resources/Textures/T_EmissionMap.png",
- SourceOpacityPath = "Assets/Synty/Tools/SidekickCharacters/Resources/Textures/T_OpacityMap.png",
+ SourceColorPath = GetPackageAssetPath("Resources", "Textures", "T_ColorMap.png"),
+ SourceMetallicPath = GetPackageAssetPath("Resources", "Textures", "T_MetallicMap.png"),
+ SourceSmoothnessPath = GetPackageAssetPath("Resources", "Textures", "T_SmoothnessMap.png"),
+ SourceReflectionPath = GetPackageAssetPath("Resources", "Textures", "T_ReflectionMap.png"),
+ SourceEmissionPath = GetPackageAssetPath("Resources", "Textures", "T_EmissionMap.png"),
+ SourceOpacityPath = GetPackageAssetPath("Resources", "Textures", "T_OpacityMap.png"),
};
newSet.Save(this);
@@ -206,5 +221,216 @@ namespace Synty.SidekickCharacters.Database
{
return new Version(GetCurrentDbConnection()?.Table().FirstOrDefault().SemanticVersion ?? "0.0.1ea");
}
+
+ ///
+ /// Gets the absolute filesystem path to the Sidekick database file.
+ ///
+ /// The absolute path to the DB file, if found.
+ public static string GetDatabasePath()
+ {
+ if (!string.IsNullOrEmpty(_databasePath) && File.Exists(_databasePath))
+ {
+ return _databasePath;
+ }
+
+ string seedDatabasePath = GetSeedDatabasePath();
+ if (string.IsNullOrEmpty(seedDatabasePath))
+ {
+ return null;
+ }
+
+ string projectRoot = Directory.GetParent(Application.dataPath)?.FullName;
+ if (string.IsNullOrEmpty(projectRoot))
+ {
+ return null;
+ }
+
+ string workingDirectory = Path.Combine(projectRoot, "Library", _WORKING_DATABASE_DIRECTORY);
+ Directory.CreateDirectory(workingDirectory);
+
+ _databasePath = Path.Combine(workingDirectory, _DATABASE_FILE_NAME);
+ if (!File.Exists(_databasePath))
+ {
+ File.Copy(seedDatabasePath, _databasePath, false);
+ }
+
+ _databasePath = Path.GetFullPath(_databasePath);
+ CleanupLegacyDatabaseArtifacts(seedDatabasePath);
+ return _databasePath;
+ }
+
+ ///
+ /// Gets the absolute filesystem path to the Sidekick package root.
+ ///
+ /// The absolute path to the package root, if found.
+ public static string GetPackageRootAbsolutePath()
+ {
+ if (!string.IsNullOrEmpty(_packageRootAbsolutePath) && Directory.Exists(_packageRootAbsolutePath))
+ {
+ return _packageRootAbsolutePath;
+ }
+
+ string seedDatabasePath = GetSeedDatabasePath();
+ if (string.IsNullOrEmpty(seedDatabasePath))
+ {
+ return null;
+ }
+
+ string databaseDirectory = Path.GetDirectoryName(seedDatabasePath);
+ if (string.IsNullOrEmpty(databaseDirectory))
+ {
+ return null;
+ }
+
+ _packageRootAbsolutePath = Path.GetDirectoryName(databaseDirectory);
+ if (!string.IsNullOrEmpty(_packageRootAbsolutePath))
+ {
+ _packageRootAbsolutePath = Path.GetFullPath(_packageRootAbsolutePath);
+ }
+
+ return _packageRootAbsolutePath;
+ }
+
+ ///
+ /// Gets the project-relative asset path to the Sidekick package root.
+ ///
+ /// The asset path to the package root, if found.
+ public static string GetPackageRootAssetPath()
+ {
+ if (!string.IsNullOrEmpty(_packageRootAssetPath))
+ {
+ return _packageRootAssetPath;
+ }
+
+ string packageRootAbsolutePath = GetPackageRootAbsolutePath();
+ if (string.IsNullOrEmpty(packageRootAbsolutePath))
+ {
+ return null;
+ }
+
+ _packageRootAssetPath = ToAssetPath(packageRootAbsolutePath);
+ return _packageRootAssetPath;
+ }
+
+ ///
+ /// Builds a project-relative asset path under the Sidekick package root.
+ ///
+ /// Path segments under the package root.
+ /// The combined asset path, if the package root could be found.
+ public static string GetPackageAssetPath(params string[] relativeSegments)
+ {
+ string packageRootAssetPath = GetPackageRootAssetPath();
+ if (string.IsNullOrEmpty(packageRootAssetPath))
+ {
+ return null;
+ }
+
+ string combinedPath = packageRootAssetPath;
+ foreach (string segment in relativeSegments)
+ {
+ combinedPath = Path.Combine(combinedPath, segment);
+ }
+
+ return NormalizePathSeparators(combinedPath);
+ }
+
+ ///
+ /// Normalizes legacy Sidekick asset paths so moved packages still load correctly.
+ ///
+ /// The asset path to normalize.
+ /// The remapped path if it pointed at an old root; otherwise the original path.
+ public static string NormalizeLegacyAssetPath(string assetPath)
+ {
+ if (string.IsNullOrWhiteSpace(assetPath))
+ {
+ return assetPath;
+ }
+
+ string normalizedPath = NormalizePathSeparators(assetPath);
+ string packageRootAssetPath = GetPackageRootAssetPath();
+ if (string.IsNullOrEmpty(packageRootAssetPath))
+ {
+ return normalizedPath;
+ }
+
+ if (normalizedPath.StartsWith(_LEGACY_TOOLS_PACKAGE_ROOT, StringComparison.OrdinalIgnoreCase))
+ {
+ return packageRootAssetPath + normalizedPath.Substring(_LEGACY_TOOLS_PACKAGE_ROOT.Length);
+ }
+
+ if (normalizedPath.StartsWith(_LEGACY_PACKAGE_ROOT, StringComparison.OrdinalIgnoreCase))
+ {
+ return packageRootAssetPath + normalizedPath.Substring(_LEGACY_PACKAGE_ROOT.Length);
+ }
+
+ return normalizedPath;
+ }
+
+ private static string ToAssetPath(string fullPath)
+ {
+ string normalizedFullPath = NormalizePathSeparators(Path.GetFullPath(fullPath));
+ string normalizedAssetsPath = NormalizePathSeparators(Path.GetFullPath(Application.dataPath));
+
+ if (!normalizedFullPath.StartsWith(normalizedAssetsPath, StringComparison.OrdinalIgnoreCase))
+ {
+ return normalizedFullPath;
+ }
+
+ return "Assets" + normalizedFullPath.Substring(normalizedAssetsPath.Length);
+ }
+
+ private static string NormalizePathSeparators(string path)
+ {
+ return path?.Replace('\\', '/');
+ }
+
+ private static string GetSeedDatabasePath()
+ {
+ if (!string.IsNullOrEmpty(_seedDatabasePath) && File.Exists(_seedDatabasePath))
+ {
+ return _seedDatabasePath;
+ }
+
+ string[] databaseFiles = Directory.GetFiles(Application.dataPath, _DATABASE_FILE_NAME, SearchOption.AllDirectories);
+ _seedDatabasePath = databaseFiles
+ .OrderBy(path => path.Contains($"{Path.DirectorySeparatorChar}SidekickCharacters{Path.DirectorySeparatorChar}Database{Path.DirectorySeparatorChar}") ? 0 : 1)
+ .FirstOrDefault();
+
+ if (!string.IsNullOrEmpty(_seedDatabasePath))
+ {
+ _seedDatabasePath = Path.GetFullPath(_seedDatabasePath);
+ }
+
+ return _seedDatabasePath;
+ }
+
+ private static void CleanupLegacyDatabaseArtifacts(string seedDatabasePath)
+ {
+ if (string.IsNullOrEmpty(seedDatabasePath))
+ {
+ return;
+ }
+
+ DeleteIfExists(seedDatabasePath + "-journal");
+ DeleteIfExists(seedDatabasePath + "-journal.meta");
+ DeleteIfExists(seedDatabasePath + "-wal");
+ DeleteIfExists(seedDatabasePath + "-wal.meta");
+ DeleteIfExists(seedDatabasePath + "-shm");
+ DeleteIfExists(seedDatabasePath + "-shm.meta");
+ }
+
+ private static void DeleteIfExists(string path)
+ {
+ try
+ {
+ if (File.Exists(path))
+ {
+ File.Delete(path);
+ }
+ }
+ catch
+ {
+ }
+ }
}
}