mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
253 lines
9.0 KiB
C#
253 lines
9.0 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using System.Threading.Tasks;
|
|
using BenchmarkDotNet.Attributes;
|
|
using Robust.Server.Containers;
|
|
using Robust.Server.GameStates;
|
|
using Robust.Shared;
|
|
using Robust.Shared.Analyzers;
|
|
using Robust.Shared.Configuration;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.Network;
|
|
using Robust.Shared.Player;
|
|
using Robust.UnitTesting;
|
|
|
|
namespace Robust.Benchmarks.Transform;
|
|
|
|
/// <summary>
|
|
/// This benchmark tests various transform/move related functions with an entity that has many children.
|
|
/// </summary>
|
|
[Virtual, MemoryDiagnoser]
|
|
public class RecursiveMoveBenchmark : RobustIntegrationTest
|
|
{
|
|
private IEntityManager _entMan = default!;
|
|
private SharedTransformSystem _transform = default!;
|
|
private ContainerSystem _container = default!;
|
|
private PvsSystem _pvs = default!;
|
|
private EntityCoordinates _mapCoords;
|
|
private EntityCoordinates _gridCoords;
|
|
private EntityCoordinates _gridCoords2;
|
|
private EntityUid _ent;
|
|
private EntityUid _child;
|
|
private TransformComponent _childXform = default!;
|
|
private EntityQuery<TransformComponent> _query;
|
|
private ICommonSession[] _players = default!;
|
|
private PvsSession _session = default!;
|
|
|
|
[GlobalSetup]
|
|
public void GlobalSetup()
|
|
{
|
|
ProgramShared.PathOffset = "../../../../";
|
|
var server = StartServer();
|
|
var client = StartClient();
|
|
|
|
Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync()).Wait();
|
|
|
|
var mapMan = server.ResolveDependency<IMapManager>();
|
|
_entMan = server.ResolveDependency<IEntityManager>();
|
|
var confMan = server.ResolveDependency<IConfigurationManager>();
|
|
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
|
|
|
|
_transform = _entMan.System<SharedTransformSystem>();
|
|
_container = _entMan.System<ContainerSystem>();
|
|
_pvs = _entMan.System<PvsSystem>();
|
|
_query = _entMan.GetEntityQuery<TransformComponent>();
|
|
var mapSys = _entMan.System<SharedMapSystem>();
|
|
|
|
var netMan = client.ResolveDependency<IClientNetManager>();
|
|
client.SetConnectTarget(server);
|
|
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
|
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
server.WaitRunTicks(1).Wait();
|
|
client.WaitRunTicks(1).Wait();
|
|
}
|
|
|
|
// Ensure client & server ticks are synced.
|
|
// Client runs 1 tick ahead
|
|
{
|
|
var sTick = (int)server.Timing.CurTick.Value;
|
|
var cTick = (int)client.Timing.CurTick.Value;
|
|
var delta = cTick - sTick;
|
|
|
|
if (delta > 1)
|
|
server.WaitRunTicks(delta - 1).Wait();
|
|
else if (delta < 1)
|
|
client.WaitRunTicks(1 - delta).Wait();
|
|
|
|
sTick = (int)server.Timing.CurTick.Value;
|
|
cTick = (int)client.Timing.CurTick.Value;
|
|
delta = cTick - sTick;
|
|
if (delta != 1)
|
|
throw new Exception("Failed setup");
|
|
}
|
|
|
|
// Set up map and spawn player
|
|
server.WaitPost(() =>
|
|
{
|
|
var map = server.ResolveDependency<SharedMapSystem>().CreateMap(out var mapId);
|
|
var gridComp = mapMan.CreateGridEntity(mapId);
|
|
var grid = gridComp.Owner;
|
|
mapSys.SetTile(grid, gridComp, Vector2i.Zero, new Tile(1));
|
|
_gridCoords = new EntityCoordinates(grid, .5f, .5f);
|
|
_gridCoords2 = new EntityCoordinates(grid, .5f, .6f);
|
|
_mapCoords = new EntityCoordinates(map, 100, 100);
|
|
|
|
var playerUid = _entMan.SpawnEntity(null, _mapCoords);
|
|
|
|
// Attach player.
|
|
var session = sPlayerMan.Sessions.First();
|
|
server.PlayerMan.SetAttachedEntity(session, playerUid);
|
|
sPlayerMan.JoinGame(session);
|
|
|
|
// Next, we will spawn our test entity. This entity will have a complex transform/container hierarchy.
|
|
// This is intended to be representative of a typical SS14 player entity, with organs. clothing, and a full backpack.
|
|
_ent = _entMan.Spawn();
|
|
|
|
// Quick check that SetCoordinates actually changes the parent as expected
|
|
// I.e., ensure that grid-traversal code doesn't just dump the entity on the map.
|
|
_transform.SetCoordinates(_ent, _gridCoords);
|
|
if (_query.GetComponent(_ent).ParentUid != _gridCoords.EntityId)
|
|
throw new Exception("Grid traversal error.");
|
|
|
|
_transform.SetCoordinates(_ent, _mapCoords);
|
|
if (_query.GetComponent(_ent).ParentUid != _mapCoords.EntityId)
|
|
throw new Exception("Grid traversal error.");
|
|
|
|
// Add 5 direct children in slots to represent clothing.
|
|
for (var i = 0; i < 5; i++)
|
|
{
|
|
var id = $"inventory{i}";
|
|
_container.EnsureContainer<ContainerSlot>(_ent, id);
|
|
if (!_entMan.TrySpawnInContainer(null, _ent, id, out _))
|
|
throw new Exception($"Failed to setup entity");
|
|
}
|
|
|
|
// body parts
|
|
_container.EnsureContainer<Container>(_ent, "body");
|
|
for (var i = 0; i < 5; i++)
|
|
{
|
|
// Simple organ
|
|
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out _))
|
|
throw new Exception($"Failed to setup entity");
|
|
|
|
// body part that has another body part / limb
|
|
if (!_entMan.TrySpawnInContainer(null, _ent, "body", out var limb))
|
|
throw new Exception($"Failed to setup entity");
|
|
|
|
_container.EnsureContainer<ContainerSlot>(limb.Value, "limb");
|
|
if (!_entMan.TrySpawnInContainer(null, limb.Value, "limb", out _))
|
|
throw new Exception($"Failed to setup entity");
|
|
}
|
|
|
|
// Backpack
|
|
_container.EnsureContainer<ContainerSlot>(_ent, "inventory-backpack");
|
|
if (!_entMan.TrySpawnInContainer(null, _ent, "inventory-backpack", out var backpack))
|
|
throw new Exception($"Failed to setup entity");
|
|
|
|
// Misc backpack contents.
|
|
var backpackStorage = _container.EnsureContainer<Container>(backpack.Value, "storage");
|
|
for (var i = 0; i < 10; i++)
|
|
{
|
|
if (!_entMan.TrySpawnInContainer(null, backpack.Value, "storage", out _))
|
|
throw new Exception($"Failed to setup entity");
|
|
}
|
|
|
|
// Emergency box inside of the backpack
|
|
var box = backpackStorage.ContainedEntities.First();
|
|
var boxContainer = _container.EnsureContainer<Container>(box, "storage");
|
|
for (var i = 0; i < 10; i++)
|
|
{
|
|
if (!_entMan.TrySpawnInContainer(null, box, "storage", out _))
|
|
throw new Exception($"Failed to setup entity");
|
|
}
|
|
|
|
// Deepest child.
|
|
_child = boxContainer.ContainedEntities.First();
|
|
_childXform = _query.GetComponent(_child);
|
|
|
|
_players = new[] {session};
|
|
_session = _pvs.PlayerData[session];
|
|
}).Wait();
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
server.WaitRunTicks(1).Wait();
|
|
client.WaitRunTicks(1).Wait();
|
|
}
|
|
|
|
PvsTick();
|
|
PvsTick();
|
|
}
|
|
|
|
private void PvsTick()
|
|
{
|
|
_session.ClearState();
|
|
_pvs.CacheSessionData(_players);
|
|
_pvs.GetVisibleChunks();
|
|
_pvs.ProcessVisibleChunksSequential();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This implicitly measures move events, including PVS and entity lookups. Though given that most of the entities
|
|
/// are in containers, this will bias the entity lookup aspect.
|
|
/// </summary>
|
|
[Benchmark]
|
|
public void MoveEntity()
|
|
{
|
|
_transform.SetCoordinates(_ent, _gridCoords);
|
|
_transform.SetCoordinates(_ent, _mapCoords);
|
|
}
|
|
|
|
[Benchmark]
|
|
public void MoveEntityASmidge()
|
|
{
|
|
_transform.SetCoordinates(_ent, _gridCoords);
|
|
_transform.SetCoordinates(_ent, _gridCoords2);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Like <see cref="MoveEntity"/>, but also processes queued PVS chunk updates.
|
|
/// </summary>
|
|
[Benchmark]
|
|
public void MoveAndUpdateChunks()
|
|
{
|
|
_transform.SetCoordinates(_ent, _gridCoords);
|
|
PvsTick();
|
|
_transform.SetCoordinates(_ent, _mapCoords);
|
|
PvsTick();
|
|
}
|
|
|
|
[Benchmark]
|
|
public void MoveASmidgeAndUpdateChunk()
|
|
{
|
|
_transform.SetCoordinates(_ent, _gridCoords);
|
|
PvsTick();
|
|
_transform.SetCoordinates(_ent, _gridCoords2);
|
|
PvsTick();
|
|
}
|
|
|
|
[Benchmark]
|
|
public Vector2 GetWorldPos()
|
|
{
|
|
return _transform.GetWorldPosition(_childXform);
|
|
}
|
|
|
|
[Benchmark]
|
|
public EntityUid GetRootUid()
|
|
{
|
|
var xform = _childXform;
|
|
while (xform.ParentUid.IsValid())
|
|
{
|
|
xform = _query.GetComponent(xform.ParentUid);
|
|
}
|
|
return xform.ParentUid;
|
|
}
|
|
}
|