(Feat) Add Build
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
using System.Diagnostics;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using Ashwild.Building;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace Ashwild.EditorTools
|
||||
{
|
||||
/// <summary>
|
||||
/// A load test for the decision to spawn every build as a NetworkObject: places a grid of structures
|
||||
/// and reports how long it took and what it cost in memory, so the "can FishNet carry a whole base"
|
||||
/// question can be answered with numbers instead of estimates.
|
||||
///
|
||||
/// Deliberately single-instance. Measuring a second client's join bandwidth would mean two peers, and
|
||||
/// the session is wired to FishySteamworks — that costs far more setup than the risk it covers. Join
|
||||
/// traffic is a one-shot cost of roughly a spawn packet per object anyway; what actually matters day
|
||||
/// to day is the steady state, and that is exactly what this measures. Watch the profiler's frame time
|
||||
/// after the spawn, not just the numbers logged here.
|
||||
///
|
||||
/// Caveat when reading the elapsed time: this places everything in a single frame, which no player
|
||||
/// ever does. Treat it as a worst case for the spawn burst, not as a gameplay measurement.
|
||||
/// </summary>
|
||||
public static class BuildStressTest
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const float Spacing = 4f;
|
||||
private const float GroundHeight = 0f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Menu
|
||||
|
||||
/// <summary>
|
||||
/// Places 250 structures — a realistic co-op base.
|
||||
/// </summary>
|
||||
[MenuItem("Tools/Ashwild/Stress Test/Spawn 250 Builds")]
|
||||
public static void Spawn250() => SpawnGrid(250);
|
||||
|
||||
/// <summary>
|
||||
/// Places 1000 structures — an ambitious long-save base, the level the NetworkObject decision was
|
||||
/// judged against.
|
||||
/// </summary>
|
||||
[MenuItem("Tools/Ashwild/Stress Test/Spawn 1000 Builds")]
|
||||
public static void Spawn1000() => SpawnGrid(1000);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Commits <paramref name="count"/> builds of the first usable buildable on a grid around the
|
||||
/// origin, then logs the elapsed time and the memory delta. Requires play mode with a live session,
|
||||
/// since committing goes through the registry's server RPC exactly as a real placement would.
|
||||
/// </summary>
|
||||
private static void SpawnGrid(int count)
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
Debug.LogError("[BuildStressTest] Enter play mode and host a session first — builds are spawned through the server.");
|
||||
return;
|
||||
}
|
||||
if (BuildRegistry.Instance == null)
|
||||
{
|
||||
Debug.LogError("[BuildStressTest] No BuildRegistry in the session — is the scene's BuildRegistry present and the session started?");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryResolveBuildable(out ushort id, out string label)) return;
|
||||
|
||||
int side = Mathf.CeilToInt(Mathf.Sqrt(count));
|
||||
long memoryBefore = Profiler.GetTotalAllocatedMemoryLong();
|
||||
Stopwatch watch = Stopwatch.StartNew();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
Vector3 position = new Vector3((i % side) * Spacing, GroundHeight, (i / side) * Spacing);
|
||||
BuildRegistry.Instance.RequestBuild(id, position, Quaternion.identity);
|
||||
}
|
||||
|
||||
watch.Stop();
|
||||
long memoryDelta = Profiler.GetTotalAllocatedMemoryLong() - memoryBefore;
|
||||
|
||||
Debug.Log($"[BuildStressTest] Requested {count}× '{label}' in {watch.ElapsedMilliseconds} ms " +
|
||||
$"({memoryDelta / 1024f / 1024f:F1} MB allocated this frame). " +
|
||||
"Spawns complete over the next frames — check the profiler's steady-state frame time now.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Picks the first buildable that can actually be spawned, so the test never fails halfway on a
|
||||
/// half-authored asset. Reports clearly when the database is empty or unbuilt.
|
||||
/// </summary>
|
||||
private static bool TryResolveBuildable(out ushort id, out string label)
|
||||
{
|
||||
id = 0;
|
||||
label = string.Empty;
|
||||
|
||||
BuildableDatabase database = BuildableDatabase.Instance;
|
||||
if (database == null || database.Buildables == null || database.Buildables.Length == 0)
|
||||
{
|
||||
Debug.LogError("[BuildStressTest] BuildableDatabase is empty — run Tools ▸ Ashwild ▸ Rebuild Buildable Database.");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (BuildableData buildable in database.Buildables)
|
||||
{
|
||||
if (buildable == null || buildable.BuiltPrefab == null) continue;
|
||||
|
||||
id = database.GetId(buildable);
|
||||
label = buildable.name;
|
||||
return true;
|
||||
}
|
||||
|
||||
Debug.LogError("[BuildStressTest] No buildable with a BuiltPrefab found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user