mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-09-01 17:47:24 +02:00
* Move MapManager queries to SharedMapSystem Moves all variants of FindGridsIntersecting/TryFindGridAt to SharedMapSystem Hollows out the MapManager methods and converts them into relays to SharedMapSystem * Move CreateGrid to SharedMapSystem Moves the functionality for CreateGrid and its variants to SharedMapSystem Hollows out the MapManager methods and converts them into relays for the SharedMapSystem methods Obsoletes them too Also moves over the GetAllMapGrids and GetAllGrids methods * Move RaiseOnTileChanged to SharedMapSystem Also moves the SuppressOnTileChanged flag member to SharedMapSystem Hollows out and obsoletes the MapManager versions * Move default value constants to SharedMapSystem * Converts map pausing events into LocalizedEntityCommands * Move MapManager related delegates/structs to SharedMapSystem Moves the GridCreationOptions struct to the same namespace as SharedMapSystem and converts it into a record struct Move the GridCallback delegates to the same namespace as SharedMapSystem * Move CullDeletionHistory to SharedMapSystem Well, that was less painful than I thought it would be * Actually obsolete the NetworkedMapManager method * Rename file * Doc comments for new SharedMapSystem methods * Doc comment * Doc comments * Fix access * Purge all references to IMapManagerInternal * Cull easy IMapManager references * The rest * Purge IMapManager * Private internal FindGridsIntersecting method * please die * 41 * notes --------- Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
252 lines
8.9 KiB
C#
252 lines
8.9 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(new() {Pool = false});
|
|
var client = StartClient(new() {Pool = false});
|
|
|
|
Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync()).Wait();
|
|
|
|
_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 = mapSys.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;
|
|
}
|
|
}
|