using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Player;
namespace Robust.UnitTesting.Server.GameStates;
public sealed class PvsSystemTests : RobustIntegrationTest
{
///
/// Checks that there are no issues when an entity changes PVS chunk location multiple times in a single tick.
///
[Test]
public async Task TestMultipleIndexChange()
{
var server = StartServer();
var client = StartClient();
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
var mapMan = server.ResolveDependency();
var sEntMan = server.ResolveDependency();
var confMan = server.ResolveDependency();
var sPlayerMan = server.ResolveDependency();
var xforms = sEntMan.System();
var maps = sEntMan.System();
var cEntMan = client.ResolveDependency();
var netMan = client.ResolveDependency();
var cPlayerMan = client.ResolveDependency();
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
client.Post(() => netMan.ClientConnect(null!, 0, null!));
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Set up map and grid
EntityUid grid = default;
EntityUid map = default;
await server.WaitPost(() =>
{
map = server.System().CreateMap(out var mapId);
var gridComp = mapMan.CreateGridEntity(mapId);
maps.SetTile(gridComp, Vector2i.Zero, new Tile(1));
grid = gridComp.Owner;
});
// Spawn player entity on grid 1
EntityUid player = default;
EntityUid other = default;
TransformComponent otherXform = default!;
var gridCoords = new EntityCoordinates(grid, new Vector2(0.5f, 0.5f));
var mapCoords = new EntityCoordinates(map, new Vector2(2, 2));
await server.WaitPost(() =>
{
player = sEntMan.SpawnEntity(null, gridCoords);
other = sEntMan.SpawnEntity(null, gridCoords);
otherXform = sEntMan.GetComponent(other);
// Ensure map PVS chunk is not empty
sEntMan.SpawnEntity(null, mapCoords);
// Attach player.
var session = sPlayerMan.Sessions.First();
server.PlayerMan.SetAttachedEntity(session, player);
sPlayerMan.JoinGame(session);
});
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Check player got properly attached
await client.WaitPost(() =>
{
var ent = cEntMan.GetNetEntity(cPlayerMan.LocalEntity);
Assert.That(ent, Is.EqualTo(sEntMan.GetNetEntity(player)));
});
// Move the player off-grid and back onto the grid in the same tick
xforms.SetCoordinates(other, otherXform, mapCoords);
xforms.SetCoordinates(other, otherXform, gridCoords);
// Run for a few ticks. The test just checks that no PVS asserts/errors happen.
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Repeat but in the opposite direction ( map -> grid -> map )
// first move to map and wait a bit.
xforms.SetCoordinates(other, otherXform, mapCoords);
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Move to and off grid in the same tick
xforms.SetCoordinates(other, otherXform, gridCoords);
xforms.SetCoordinates(other, otherXform, mapCoords);
// wait for errors.
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
await client.WaitPost(() => netMan.ClientDisconnect(""));
await server.WaitRunTicks(5);
await client.WaitRunTicks(5);
}
}