2022-05-22 20:31:39 -07:00
using System ;
2022-06-19 20:22:28 -07:00
using System.Collections.Generic ;
2022-05-22 20:31:39 -07:00
using System.IO ;
2020-09-02 01:16:42 +02:00
using System.Linq ;
2022-06-19 20:22:28 -07:00
using System.Threading ;
2020-09-02 01:16:42 +02:00
using System.Threading.Tasks ;
2022-09-14 19:02:59 +10:00
using Content.Server.GameTicking ;
using Content.Server.Maps ;
using Content.Server.Shuttles.Components ;
2022-10-15 22:18:48 +02:00
using Content.Server.Spawners.Components ;
2022-09-14 19:02:59 +10:00
using Content.Server.Station.Components ;
2022-10-15 22:18:48 +02:00
using Content.Server.Station.Systems ;
using Content.Shared.Roles ;
2020-09-02 01:16:42 +02:00
using NUnit.Framework ;
2022-11-13 17:47:48 +11:00
using Robust.Server.GameObjects ;
2021-11-12 02:42:27 +00:00
using Robust.Server.Maps ;
2021-02-11 01:13:03 -08:00
using Robust.Shared.ContentPack ;
2022-09-14 19:02:59 +10:00
using Robust.Shared.GameObjects ;
2020-09-02 01:16:42 +02:00
using Robust.Shared.Utility ;
2021-11-12 02:42:27 +00:00
using Robust.Shared.Map ;
2022-09-14 19:02:59 +10:00
using Robust.Shared.Prototypes ;
2020-09-02 01:16:42 +02:00
using YamlDotNet.RepresentationModel ;
2022-09-14 19:02:59 +10:00
using ShuttleSystem = Content . Server . Shuttles . Systems . ShuttleSystem ;
2020-09-02 01:16:42 +02:00
namespace Content.IntegrationTests.Tests
{
[TestFixture]
2022-06-19 20:22:28 -07:00
public sealed class PostMapInitTest
2020-09-02 01:16:42 +02:00
{
2022-07-20 14:48:44 +10:00
private const bool SkipTestMaps = true ;
private const string TestMapsPath = "/Maps/Test/" ;
2020-09-02 01:16:42 +02:00
2022-10-17 14:24:48 +11:00
private static readonly string [ ] NoSpawnMaps =
{
"CentComm" ,
"Dart" ,
} ;
2022-10-17 03:44:56 +11:00
private static string [ ] Grids =
{
"/Maps/centcomm.yml" ,
"/Maps/Shuttles/cargo.yml" ,
"/Maps/Shuttles/emergency.yml" ,
"/Maps/infiltrator.yml" ,
} ;
/// <summary>
/// Asserts that specific files have been saved as grids and not maps.
/// </summary>
[Test, TestCaseSource(nameof(Grids))]
public async Task GridsLoadableTest ( string mapFile )
{
await using var pairTracker = await PoolManager . GetServerClient ( new PoolSettings { NoClient = true } ) ;
var server = pairTracker . Pair . Server ;
2022-11-13 17:47:48 +11:00
var mapLoader = server . ResolveDependency < IEntitySystemManager > ( ) . GetEntitySystem < MapLoaderSystem > ( ) ;
2022-10-17 03:44:56 +11:00
var mapManager = server . ResolveDependency < IMapManager > ( ) ;
await server . WaitPost ( ( ) = >
{
var mapId = mapManager . CreateMap ( ) ;
try
{
mapLoader . LoadGrid ( mapId , mapFile ) ;
}
catch ( Exception ex )
{
throw new Exception ( $"Failed to load map {mapFile}, was it saved as a map instead of a grid?" , ex ) ;
}
try
{
mapManager . DeleteMap ( mapId ) ;
}
catch ( Exception ex )
{
throw new Exception ( $"Failed to delete map {mapFile}" , ex ) ;
}
} ) ;
await server . WaitRunTicks ( 1 ) ;
await pairTracker . CleanReturnAsync ( ) ;
}
2020-09-02 01:16:42 +02:00
[Test]
public async Task NoSavedPostMapInitTest ( )
{
2022-06-19 20:22:28 -07:00
await using var pairTracker = await PoolManager . GetServerClient ( new PoolSettings { NoClient = true } ) ;
var server = pairTracker . Pair . Server ;
2020-09-02 01:16:42 +02:00
var resourceManager = server . ResolveDependency < IResourceManager > ( ) ;
var mapFolder = new ResourcePath ( "/Maps" ) ;
var maps = resourceManager
. ContentFindFiles ( mapFolder )
2023-01-19 03:56:45 +01:00
. Where ( filePath = > filePath . Extension = = "yml" & & ! filePath . Filename . StartsWith ( "." , StringComparison . Ordinal ) )
2020-09-02 01:16:42 +02:00
. ToArray ( ) ;
foreach ( var map in maps )
{
var rootedPath = map . ToRootedPath ( ) ;
2020-09-12 15:47:57 +02:00
// ReSharper disable once RedundantLogicalConditionalExpressionOperand
2023-01-19 03:56:45 +01:00
if ( SkipTestMaps & & rootedPath . ToString ( ) . StartsWith ( TestMapsPath , StringComparison . Ordinal ) )
2020-09-02 01:16:42 +02:00
{
continue ;
}
if ( ! resourceManager . TryContentFileRead ( rootedPath , out var fileStream ) )
{
Assert . Fail ( $"Map not found: {rootedPath}" ) ;
}
using var reader = new StreamReader ( fileStream ) ;
var yamlStream = new YamlStream ( ) ;
yamlStream . Load ( reader ) ;
var root = yamlStream . Documents [ 0 ] . RootNode ;
var meta = root [ "meta" ] ;
var postMapInit = meta [ "postmapinit" ] . AsBool ( ) ;
2020-09-12 15:47:57 +02:00
Assert . False ( postMapInit , $"Map {map.Filename} was saved postmapinit" ) ;
2020-09-02 01:16:42 +02:00
}
2022-06-19 20:22:28 -07:00
await pairTracker . CleanReturnAsync ( ) ;
2020-09-02 01:16:42 +02:00
}
2021-11-12 02:42:27 +00:00
2022-09-14 19:02:59 +10:00
private static string [ ] GetGameMapNames ( )
2021-11-12 02:42:27 +00:00
{
2022-07-20 14:48:44 +10:00
Task < string [ ] > task ;
2022-06-19 20:22:28 -07:00
using ( ExecutionContext . SuppressFlow ( ) )
{
task = Task . Run ( static async ( ) = >
{
await Task . Yield ( ) ;
2022-08-27 19:55:31 -07:00
await using var pairTracker = await PoolManager . GetServerClient (
new PoolSettings
{
Disconnected = true ,
2022-09-14 19:02:59 +10:00
TestName = $"{nameof(PostMapInitTest)}.{nameof(GetGameMapNames)}"
2022-08-27 19:55:31 -07:00
}
) ;
2022-06-19 20:22:28 -07:00
var server = pairTracker . Pair . Server ;
2022-09-14 19:02:59 +10:00
var protoManager = server . ResolveDependency < IPrototypeManager > ( ) ;
var maps = protoManager . EnumeratePrototypes < GameMapPrototype > ( ) . ToList ( ) ;
var mapNames = new List < string > ( ) ;
var naughty = new HashSet < string > ( )
{
2022-10-04 14:33:14 +11:00
"Empty" ,
"Infiltrator" ,
"Pirate" ,
2022-09-14 19:02:59 +10:00
} ;
foreach ( var map in maps )
{
// AAAAAAAAAA
// Why are they stations!
if ( naughty . Contains ( map . ID ) )
continue ;
mapNames . Add ( map . ID ) ;
}
await pairTracker . CleanReturnAsync ( ) ;
return mapNames . ToArray ( ) ;
} ) ;
Task . WaitAll ( task ) ;
}
return task . GetAwaiter ( ) . GetResult ( ) ;
}
[Test, TestCaseSource(nameof(GetGameMapNames))]
public async Task GameMapsLoadableTest ( string mapProto )
{
await using var pairTracker = await PoolManager . GetServerClient ( new PoolSettings { NoClient = true } ) ;
var server = pairTracker . Pair . Server ;
var mapManager = server . ResolveDependency < IMapManager > ( ) ;
var entManager = server . ResolveDependency < IEntityManager > ( ) ;
2022-11-13 17:47:48 +11:00
var mapLoader = entManager . System < MapLoaderSystem > ( ) ;
2022-09-14 19:02:59 +10:00
var protoManager = server . ResolveDependency < IPrototypeManager > ( ) ;
var ticker = entManager . EntitySysManager . GetEntitySystem < GameTicker > ( ) ;
var shuttleSystem = entManager . EntitySysManager . GetEntitySystem < ShuttleSystem > ( ) ;
2022-10-17 14:24:48 +11:00
var xformQuery = entManager . GetEntityQuery < TransformComponent > ( ) ;
2022-09-14 19:02:59 +10:00
await server . WaitPost ( ( ) = >
{
var mapId = mapManager . CreateMap ( ) ;
try
{
ticker . LoadGameMap ( protoManager . Index < GameMapPrototype > ( mapProto ) , mapId , null ) ;
}
catch ( Exception ex )
{
throw new Exception ( $"Failed to load map {mapProto}" , ex ) ;
}
var shuttleMap = mapManager . CreateMap ( ) ;
var largest = 0f ;
EntityUid ? targetGrid = null ;
var memberQuery = entManager . GetEntityQuery < StationMemberComponent > ( ) ;
2022-10-17 14:24:48 +11:00
var grids = mapManager . GetAllMapGrids ( mapId ) . ToList ( ) ;
2022-12-12 14:59:02 +11:00
var gridUids = grids . Select ( o = > o . Owner ) . ToList ( ) ;
2022-09-14 19:02:59 +10:00
foreach ( var grid in grids )
{
2022-12-12 14:59:02 +11:00
if ( ! memberQuery . HasComponent ( grid . Owner ) )
2022-09-14 19:02:59 +10:00
continue ;
var area = grid . LocalAABB . Width * grid . LocalAABB . Height ;
if ( area > largest )
{
largest = area ;
2022-12-12 14:59:02 +11:00
targetGrid = grid . Owner ;
2022-09-14 19:02:59 +10:00
}
}
// Test shuttle can dock.
// This is done inside gamemap test because loading the map takes ages and we already have it.
var station = entManager . GetComponent < StationMemberComponent > ( targetGrid ! . Value ) . Station ;
2022-11-24 13:28:03 -05:00
var stationConfig = entManager . GetComponent < StationDataComponent > ( station ) . StationConfig ;
Assert . IsNotNull ( stationConfig , $"{entManager.ToPrettyString(station)} had null StationConfig." ) ;
var shuttlePath = stationConfig . EmergencyShuttlePath . ToString ( ) ;
var shuttle = mapLoader . LoadGrid ( shuttleMap , shuttlePath ) ;
2022-11-13 17:47:48 +11:00
Assert . That ( shuttle ! = null & & shuttleSystem . TryFTLDock ( entManager . GetComponent < ShuttleComponent > ( shuttle . Value ) , targetGrid . Value ) , $"Unable to dock {shuttlePath} to {mapProto}" ) ;
2022-09-14 19:02:59 +10:00
mapManager . DeleteMap ( shuttleMap ) ;
2022-10-17 14:24:48 +11:00
// Test that the map has valid latejoin spawn points
if ( ! NoSpawnMaps . Contains ( mapProto ) )
{
var lateSpawns = 0 ;
foreach ( var comp in entManager . EntityQuery < SpawnPointComponent > ( true ) )
{
if ( comp . SpawnType ! = SpawnPointType . LateJoin | |
! xformQuery . TryGetComponent ( comp . Owner , out var xform ) | |
xform . GridUid = = null | |
! gridUids . Contains ( xform . GridUid . Value ) )
{
continue ;
}
lateSpawns + + ;
break ;
}
Assert . That ( lateSpawns , Is . GreaterThan ( 0 ) , $"Found no latejoin spawn points on {mapProto}" ) ;
}
2022-10-15 22:18:48 +02:00
// Test all availableJobs have spawnPoints
// This is done inside gamemap test because loading the map takes ages and we already have it.
var jobList = entManager . GetComponent < StationJobsComponent > ( station ) . RoundStartJobList
. Where ( x = > x . Value ! = 0 )
. Select ( x = > x . Key ) ;
var spawnPoints = entManager . EntityQuery < SpawnPointComponent > ( )
. Where ( spawnpoint = > spawnpoint . SpawnType = = SpawnPointType . Job )
. Select ( spawnpoint = > spawnpoint . Job . ID )
. Distinct ( ) ;
2023-01-19 03:56:45 +01:00
List < string > missingSpawnPoints = new ( ) ;
2022-10-15 22:18:48 +02:00
foreach ( var spawnpoint in jobList . Except ( spawnPoints ) )
{
if ( protoManager . Index < JobPrototype > ( spawnpoint ) . SetPreference )
missingSpawnPoints . Add ( spawnpoint ) ;
}
Assert . That ( missingSpawnPoints . Count ( ) = = 0 , $"There is no spawnpoint for {String.Join(" , ", missingSpawnPoints)} on {mapProto}." ) ;
2022-09-14 19:02:59 +10:00
try
{
mapManager . DeleteMap ( mapId ) ;
}
catch ( Exception ex )
{
throw new Exception ( $"Failed to delete map {mapProto}" , ex ) ;
}
} ) ;
await server . WaitRunTicks ( 1 ) ;
await pairTracker . CleanReturnAsync ( ) ;
}
/// <summary>
/// Get the non-game map maps.
/// </summary>
private static string [ ] GetMaps ( )
{
Task < string [ ] > task ;
using ( ExecutionContext . SuppressFlow ( ) )
{
task = Task . Run ( static async ( ) = >
{
await Task . Yield ( ) ;
await using var pairTracker = await PoolManager . GetServerClient ( new PoolSettings { Disconnected = true } ) ;
var server = pairTracker . Pair . Server ;
2022-06-19 20:22:28 -07:00
var resourceManager = server . ResolveDependency < IResourceManager > ( ) ;
2022-09-14 19:02:59 +10:00
var protoManager = server . ResolveDependency < IPrototypeManager > ( ) ;
var gameMaps = protoManager . EnumeratePrototypes < GameMapPrototype > ( ) . Select ( o = > o . MapPath ) . ToHashSet ( ) ;
2022-06-19 20:22:28 -07:00
var mapFolder = new ResourcePath ( "/Maps" ) ;
var maps = resourceManager
. ContentFindFiles ( mapFolder )
2023-01-19 03:56:45 +01:00
. Where ( filePath = > filePath . Extension = = "yml" & & ! filePath . Filename . StartsWith ( "." , StringComparison . Ordinal ) )
2022-06-19 20:22:28 -07:00
. ToArray ( ) ;
var mapNames = new List < string > ( ) ;
foreach ( var map in maps )
{
var rootedPath = map . ToRootedPath ( ) ;
// ReSharper disable once RedundantLogicalConditionalExpressionOperand
2023-01-19 03:56:45 +01:00
if ( SkipTestMaps & & rootedPath . ToString ( ) . StartsWith ( TestMapsPath , StringComparison . Ordinal ) | |
2022-09-14 19:02:59 +10:00
gameMaps . Contains ( map ) )
2022-06-19 20:22:28 -07:00
{
continue ;
}
mapNames . Add ( rootedPath . ToString ( ) ) ;
}
await pairTracker . CleanReturnAsync ( ) ;
return mapNames . ToArray ( ) ;
} ) ;
Task . WaitAll ( task ) ;
}
return task . GetAwaiter ( ) . GetResult ( ) ;
}
2021-11-12 02:42:27 +00:00
2022-09-14 19:02:59 +10:00
[Test, TestCaseSource(nameof(GetMaps))]
2022-06-19 20:22:28 -07:00
public async Task MapsLoadableTest ( string mapName )
{
await using var pairTracker = await PoolManager . GetServerClient ( new PoolSettings { NoClient = true } ) ;
var server = pairTracker . Pair . Server ;
2021-11-12 02:42:27 +00:00
2022-11-13 17:47:48 +11:00
var mapLoader = server . ResolveDependency < IEntitySystemManager > ( ) . GetEntitySystem < MapLoaderSystem > ( ) ;
2021-11-12 02:42:27 +00:00
var mapManager = server . ResolveDependency < IMapManager > ( ) ;
2022-06-19 20:22:28 -07:00
await server . WaitPost ( ( ) = >
2021-11-12 02:42:27 +00:00
{
2022-06-19 20:22:28 -07:00
var mapId = mapManager . CreateMap ( ) ;
try
2021-11-12 02:42:27 +00:00
{
2022-06-19 20:22:28 -07:00
mapLoader . LoadMap ( mapId , mapName ) ;
}
catch ( Exception ex )
{
throw new Exception ( $"Failed to load map {mapName}" , ex ) ;
2021-11-12 02:42:27 +00:00
}
2022-06-19 20:22:28 -07:00
try
2021-11-12 02:42:27 +00:00
{
2022-06-19 20:22:28 -07:00
mapManager . DeleteMap ( mapId ) ;
}
catch ( Exception ex )
{
throw new Exception ( $"Failed to delete map {mapName}" , ex ) ;
}
} ) ;
await server . WaitRunTicks ( 1 ) ;
await pairTracker . CleanReturnAsync ( ) ;
2021-11-12 02:42:27 +00:00
}
2020-09-02 01:16:42 +02:00
}
}