110 lines
4.4 KiB
C#
110 lines
4.4 KiB
C#
using UnityEditor;
|
|
using UnityEngine;
|
|
using Ashwild.Building;
|
|
using FishNet.Object;
|
|
|
|
namespace Ashwild.EditorTools
|
|
{
|
|
/// <summary>
|
|
/// One-shot migration for the move to server-spawned builds: every committed structure is now a real
|
|
/// NetworkObject, so each buildable's BuiltPrefab needs a NetworkObject and a BuiltStructure on its
|
|
/// root. Prefabs authored before that change have neither (or only BuiltStructure, which is now a
|
|
/// NetworkBehaviour and cannot function alone), and a missing NetworkObject makes the prefab silently
|
|
/// unspawnable — the exact failure that used to leave a placed chest deactivated.
|
|
///
|
|
/// Safe to re-run: it only touches prefabs that are actually missing a component, and reports what it
|
|
/// changed. Kept as an explicit menu action rather than an automatic postprocessor because it rewrites
|
|
/// authored assets, which should never happen behind the designer's back.
|
|
/// </summary>
|
|
public static class BuiltPrefabMigrator
|
|
{
|
|
#region Menu
|
|
|
|
/// <summary>
|
|
/// Scans every BuildableData, ensures its BuiltPrefab carries NetworkObject + BuiltStructure, and
|
|
/// asks FishNet to rescan its spawnable prefab collection afterwards.
|
|
/// </summary>
|
|
[MenuItem("Tools/Ashwild/Migrate Built Prefabs To Network Objects")]
|
|
public static void Migrate()
|
|
{
|
|
string[] guids = AssetDatabase.FindAssets("t:BuildableData");
|
|
int migrated = 0;
|
|
int alreadyFine = 0;
|
|
int missingPrefab = 0;
|
|
|
|
foreach (string guid in guids)
|
|
{
|
|
BuildableData buildable = AssetDatabase.LoadAssetAtPath<BuildableData>(AssetDatabase.GUIDToAssetPath(guid));
|
|
if (buildable == null) continue;
|
|
|
|
if (buildable.BuiltPrefab == null)
|
|
{
|
|
Debug.LogWarning($"[BuiltPrefabMigrator] '{buildable.name}' has no BuiltPrefab — skipped.", buildable);
|
|
missingPrefab++;
|
|
continue;
|
|
}
|
|
|
|
if (EnsureNetworked(buildable.BuiltPrefab)) migrated++;
|
|
else alreadyFine++;
|
|
}
|
|
|
|
AssetDatabase.SaveAssets();
|
|
AssetDatabase.Refresh();
|
|
RefreshNetworkPrefabRegistry();
|
|
|
|
Debug.Log($"[BuiltPrefabMigrator] Done — {migrated} prefab(s) migrated, {alreadyFine} already correct, {missingPrefab} buildable(s) without a BuiltPrefab.");
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Internal Helpers
|
|
|
|
/// <summary>
|
|
/// Adds whatever the prefab root is missing and saves it. The NetworkObject goes on first so that
|
|
/// BuiltStructure's RequireComponent is already satisfied when it is added. Returns true when the
|
|
/// prefab was actually changed.
|
|
/// </summary>
|
|
private static bool EnsureNetworked(GameObject prefab)
|
|
{
|
|
string path = AssetDatabase.GetAssetPath(prefab);
|
|
if (string.IsNullOrEmpty(path)) return false;
|
|
|
|
GameObject root = PrefabUtility.LoadPrefabContents(path);
|
|
bool changed = false;
|
|
|
|
if (root.GetComponent<NetworkObject>() == null)
|
|
{
|
|
root.AddComponent<NetworkObject>();
|
|
changed = true;
|
|
}
|
|
if (root.GetComponent<BuiltStructure>() == null)
|
|
{
|
|
root.AddComponent<BuiltStructure>();
|
|
changed = true;
|
|
}
|
|
|
|
if (changed)
|
|
{
|
|
PrefabUtility.SaveAsPrefabAsset(root, path);
|
|
Debug.Log($"[BuiltPrefabMigrator] Migrated '{path}'.", AssetDatabase.LoadAssetAtPath<GameObject>(path));
|
|
}
|
|
|
|
PrefabUtility.UnloadPrefabContents(root);
|
|
return changed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Forces FishNet to rescan DefaultPrefabObjects so the migrated prefabs are spawnable. Invoked
|
|
/// through the menu item because the generator API is internal to the FishNet assembly.
|
|
/// </summary>
|
|
private static void RefreshNetworkPrefabRegistry()
|
|
{
|
|
const string menu = "Tools/Fish-Networking/Utility/Refresh Default Prefabs";
|
|
if (!EditorApplication.ExecuteMenuItem(menu))
|
|
Debug.LogWarning($"[BuiltPrefabMigrator] Could not run '{menu}' — run it by hand so the built prefabs are registered as spawnable.");
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|