You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
HighGroundRoyaleFusion/Assets/Photon/Fusion/Editor/NetworkProjectConfigImporte...

231 lines
8.1 KiB
C#

namespace Fusion.Editor {
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.AssetImporters;
using UnityEditor.PackageManager;
using UnityEngine;
[ScriptedImporter(3, ExtensionWithoutDot, ImportQueueOffset)]
public class NetworkProjectConfigImporter : ScriptedImporter {
public const string ExtensionWithoutDot = "fusion";
public const string Extension = "." + ExtensionWithoutDot;
public const int ImportQueueOffset = 1000;
public const string FusionPrefabTag = "FusionPrefab";
public const string FusionPrefabTagSearchTerm = "l:FusionPrefab";
public const string ScriptOrderDependencyName = "Fusion.ScriptOrderDependency";
public const string AddressablesDependencyName = "Fusion.AddressablesDependency";
public const string PrefabsDependencyName = "Fusion.PrefabsDependency";
[Header("Prefabs")]
[DrawInline]
public NetworkPrefabTableOptions PrefabOptions;
#if FUSION_ENABLE_ADDRESSABLES && !FUSION_DISABLE_ADDRESSABLES
[InitializeOnLoadMethod]
static void RegisterAddressableEventListeners() {
AssetDatabaseUtils.AddAddressableAssetsWithLabelMonitor(FusionPrefabTag, (hash) => {
AssetDatabase.RegisterCustomDependency(AddressablesDependencyName, hash);
});
}
#endif
public override void OnImportAsset(AssetImportContext ctx) {
FusionEditorLog.TraceImport(ctx.assetPath, "Staring scripted import");
NetworkProjectConfig.UnloadGlobal();
NetworkProjectConfig config = LoadConfigFromFile(ctx.assetPath);
var root = ScriptableObject.CreateInstance<NetworkProjectConfigAsset>();
root.Config = config;
ctx.AddObjectToAsset("root", root);
root.Prefabs = DiscoverPrefabs(ctx);
root.BehaviourMeta = CreateBehaviourMeta(ctx);
root.PrefabOptions = PrefabOptions;
ctx.DependsOnCustomDependency(AddressablesDependencyName);
ctx.DependsOnCustomDependency(ScriptOrderDependencyName);
ctx.DependsOnCustomDependency(PrefabsDependencyName);
}
public static NetworkProjectConfig LoadConfigFromFile(string path) {
var config = new NetworkProjectConfig();
try {
var text = File.ReadAllText(path);
if (string.IsNullOrWhiteSpace(text)) {
throw new System.ArgumentException("Empty string");
}
EditorJsonUtility.FromJsonOverwrite(text, config);
} catch (System.ArgumentException ex) {
throw new System.ArgumentException($"Failed to parse {path}: {ex.Message}");
}
return config;
}
private static List<INetworkPrefabSource> DiscoverPrefabs(AssetImportContext ctx) {
var result = new List<INetworkPrefabSource>();
var factory = new NetworkAssetSourceFactory();
var detailsLog = new System.Text.StringBuilder();
var paths = new List<string>();
foreach (var it in AssetDatabaseUtils.IterateAssets<GameObject>(label: FusionPrefabTag)) {
var prefabPath = AssetDatabase.GetAssetPath(it.instanceID);
var context = new NetworkAssetSourceFactoryContext(it);
INetworkPrefabSource source = factory.TryCreatePrefabSource(context);
if (source == null) {
ctx.LogImportError($"Unable to create prefab asset for {AssetDatabase.GetAssetPath(it.instanceID)} ({it.guid})");
continue;
}
#if FUSION_EDITOR_TRACE
detailsLog.AppendLine($"{assetPath} -> {((INetworkPrefabSource)prefabSource).EditorSummary}");
#endif
var index = paths.BinarySearch(prefabPath, StringComparer.Ordinal);
if (index < 0) {
index = ~index;
} else {
ctx.LogImportWarning($"Prefab with path {prefabPath} already added");
}
paths.Insert(index, prefabPath);
result.Insert(index, source);
}
FusionEditorLog.TraceImport($"Discover prefabs details [{result.Count}] :\n{detailsLog}");
return result;
}
private NetworkProjectConfigAsset.SerializableSimulationBehaviourMeta[] CreateBehaviourMeta(AssetImportContext ctx) {
var result = new List<NetworkProjectConfigAsset.SerializableSimulationBehaviourMeta>();
foreach (var monoScript in MonoImporter.GetAllRuntimeMonoScripts()) {
var scriptType = monoScript.GetClass();
if (scriptType?.IsSubclassOf(typeof(SimulationBehaviour)) != true) {
continue;
}
var executionOrder = MonoImporter.GetExecutionOrder(monoScript);
if (executionOrder == 0) {
// no need to add it to the list
continue;
}
result.Add(new() {
Type = scriptType,
ExecutionOrder = executionOrder
});
}
return result.OrderBy(x => x.ExecutionOrder).ToArray();
}
class Postprocessor : AssetPostprocessor {
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) {
foreach (var path in deletedAssets) {
if (path.EndsWith(Extension, StringComparison.OrdinalIgnoreCase)) {
NetworkProjectConfig.UnloadGlobal();
break;
}
}
foreach (var path in movedAssets) {
if (path.EndsWith(Extension, StringComparison.OrdinalIgnoreCase)) {
NetworkProjectConfig.UnloadGlobal();
break;
}
}
foreach (var path in importedAssets) {
if (HasSimulationBehaviours(path)) {
EditorApplication.delayCall -= RefreshScriptOrderDependencyHash;
EditorApplication.delayCall += RefreshScriptOrderDependencyHash;
break;
}
}
}
private static bool HasSimulationBehaviours(string path) {
if (path.EndsWith(".cs", StringComparison.OrdinalIgnoreCase)) {
// check if there is MB in there (with MonoImporter) and if it is a simulation behaviour
var importer = AssetImporter.GetAtPath(path) as MonoImporter;
if (importer == null) {
return false;
}
var scriptType = importer.GetScript()?.GetClass();
if (scriptType?.IsSubclassOf(typeof(SimulationBehaviour)) != true) {
return false;
}
return true;
}
if (path.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) {
// check if there is MB in there (with MonoImporter) and if it is a simulation behaviour
foreach (var asset in AssetDatabase.LoadAllAssetsAtPath(path)) {
if (asset is MonoScript monoScript) {
var scriptType = monoScript.GetClass();
if (scriptType?.IsSubclassOf(typeof(SimulationBehaviour)) == true) {
return true;
}
}
}
return false;
}
return false;
}
private static void RefreshScriptOrderDependencyHash() {
var hash = CalculateScriptOrderDependencyHash();
FusionEditorLog.TraceImport($"Refreshing {ScriptOrderDependencyName} dependency hash: {hash}");
AssetDatabase.RegisterCustomDependency(ScriptOrderDependencyName, hash);
AssetDatabase.Refresh();
}
private static Hash128 CalculateScriptOrderDependencyHash() {
var hash = new Hash128();
var scripts = MonoImporter.GetAllRuntimeMonoScripts();
foreach (var monoScript in scripts) {
var scriptType = monoScript.GetClass();
if (scriptType?.IsSubclassOf(typeof(SimulationBehaviour)) != true) {
continue;
}
var executionOrder = MonoImporter.GetExecutionOrder(monoScript);
hash.Append(executionOrder);
}
return hash;
}
}
public static void RefreshNetworkObjectPrefabHash() {
var hash = new Hash128();
foreach (var it in AssetDatabaseUtils.IterateAssets<GameObject>(label: FusionPrefabTag)) {
hash.Append(it.guid);
}
FusionEditorLog.TraceImport($"Refreshing {PrefabsDependencyName} dependency hash: {hash}");
AssetDatabase.RegisterCustomDependency(PrefabsDependencyName, hash);
AssetDatabase.Refresh();
}
}
}