| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- using System.Collections.Generic;
- using System.Linq;
- using Content.Server.Maps;
- using Content.Server.Station.Components;
- using Content.Server.Station.Systems;
- using Content.Shared.Preferences;
- using Content.Shared.Roles;
- using Robust.Shared.GameObjects;
- using Robust.Shared.Log;
- using Robust.Shared.Network;
- using Robust.Shared.Prototypes;
- using Robust.Shared.Timing;
- namespace Content.IntegrationTests.Tests.Station;
- [TestFixture]
- [TestOf(typeof(StationJobsSystem))]
- public sealed class StationJobsTest
- {
- [TestPrototypes]
- private const string Prototypes = @"
- - type: playTimeTracker
- id: PlayTimeDummyAssistant
- - type: playTimeTracker
- id: PlayTimeDummyMime
- - type: playTimeTracker
- id: PlayTimeDummyClown
- - type: playTimeTracker
- id: PlayTimeDummyCaptain
- - type: playTimeTracker
- id: PlayTimeDummyChaplain
- - type: gameMap
- id: FooStation
- minPlayers: 0
- mapName: FooStation
- mapPath: /Maps/civ/nomads_classic.yml
- stations:
- Station:
- mapNameTemplate: FooStation
- stationProto: StandardStationArena
- components:
- - type: StationJobs
- availableJobs:
- TMime: [0, -1]
- TAssistant: [-1, -1]
- TCaptain: [5, 5]
- TClown: [5, 6]
- - type: job
- id: TAssistant
- playTimeTracker: PlayTimeDummyAssistant
- - type: job
- id: TMime
- weight: 20
- playTimeTracker: PlayTimeDummyMime
- - type: job
- id: TClown
- weight: -10
- playTimeTracker: PlayTimeDummyClown
- - type: job
- id: TCaptain
- weight: 10
- playTimeTracker: PlayTimeDummyCaptain
- - type: job
- id: TChaplain
- playTimeTracker: PlayTimeDummyChaplain
- ";
- private const int StationCount = 100;
- private const int CaptainCount = StationCount;
- private const int PlayerCount = 2000;
- private const int TotalPlayers = PlayerCount + CaptainCount;
- [Test]
- public async Task AssignJobsTest()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var prototypeManager = server.ResolveDependency<IPrototypeManager>();
- var fooStationProto = prototypeManager.Index<GameMapPrototype>("FooStation");
- var entSysMan = server.ResolveDependency<IEntityManager>().EntitySysManager;
- var stationJobs = entSysMan.GetEntitySystem<StationJobsSystem>();
- var stationSystem = entSysMan.GetEntitySystem<StationSystem>();
- var logmill = server.ResolveDependency<ILogManager>().RootSawmill;
- List<EntityUid> stations = new();
- await server.WaitPost(() =>
- {
- for (var i = 0; i < StationCount; i++)
- {
- stations.Add(stationSystem.InitializeNewStation(fooStationProto.Stations["Station"], null, $"Foo {StationCount}"));
- }
- });
- await server.WaitAssertion(() =>
- {
- var fakePlayers = new Dictionary<NetUserId, HumanoidCharacterProfile>()
- .AddJob("TAssistant", JobPriority.Medium, PlayerCount)
- .AddPreference("TClown", JobPriority.Low)
- .AddPreference("TMime", JobPriority.High)
- .WithPlayers(
- new Dictionary<NetUserId, HumanoidCharacterProfile>()
- .AddJob("TCaptain", JobPriority.High, CaptainCount)
- );
- Assert.That(fakePlayers, Is.Not.Empty);
- var start = new Stopwatch();
- start.Start();
- var assigned = stationJobs.AssignJobs(fakePlayers, stations);
- Assert.That(assigned, Is.Not.Empty);
- var time = start.Elapsed.TotalMilliseconds;
- logmill.Info($"Took {time} ms to distribute {TotalPlayers} players.");
- Assert.Multiple(() =>
- {
- foreach (var station in stations)
- {
- var assignedHere = assigned
- .Where(x => x.Value.Item2 == station)
- .ToDictionary(x => x.Key, x => x.Value);
- // Each station should have SOME players.
- Assert.That(assignedHere, Is.Not.Empty);
- // And it should have at least the minimum players to be considered a "fair" share, as they're all the same.
- Assert.That(assignedHere, Has.Count.GreaterThanOrEqualTo(TotalPlayers / stations.Count), "Station has too few players.");
- // And it shouldn't have ALL the players, either.
- Assert.That(assignedHere, Has.Count.LessThan(TotalPlayers), "Station has too many players.");
- // And there should be *A* captain, as there's one player with captain enabled per station.
- Assert.That(assignedHere.Where(x => x.Value.Item1 == "TCaptain").ToList(), Has.Count.EqualTo(1));
- }
- // All clown players have assistant as a higher priority.
- Assert.That(assigned.Values.Select(x => x.Item1).ToList(), Does.Not.Contain("TClown"));
- // Mime isn't an open job-slot at round-start.
- Assert.That(assigned.Values.Select(x => x.Item1).ToList(), Does.Not.Contain("TMime"));
- // All players have slots they can fill.
- Assert.That(assigned.Values, Has.Count.EqualTo(TotalPlayers), $"Expected {TotalPlayers} players.");
- // There must be assistants present.
- Assert.That(assigned.Values.Select(x => x.Item1).ToList(), Does.Contain("TAssistant"));
- // There must be captains present, too.
- Assert.That(assigned.Values.Select(x => x.Item1).ToList(), Does.Contain("TCaptain"));
- });
- });
- await pair.CleanReturnAsync();
- }
- [Test]
- public async Task AdjustJobsTest()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var prototypeManager = server.ResolveDependency<IPrototypeManager>();
- var fooStationProto = prototypeManager.Index<GameMapPrototype>("FooStation");
- var entSysMan = server.ResolveDependency<IEntityManager>().EntitySysManager;
- var stationJobs = entSysMan.GetEntitySystem<StationJobsSystem>();
- var stationSystem = entSysMan.GetEntitySystem<StationSystem>();
- var station = EntityUid.Invalid;
- await server.WaitPost(() =>
- {
- station = stationSystem.InitializeNewStation(fooStationProto.Stations["Station"], null, $"Foo Station");
- });
- await server.WaitRunTicks(1);
- await server.WaitAssertion(() =>
- {
- // Verify jobs are/are not unlimited.
- Assert.Multiple(() =>
- {
- Assert.That(stationJobs.IsJobUnlimited(station, "TAssistant"), "TAssistant is expected to be unlimited.");
- Assert.That(stationJobs.IsJobUnlimited(station, "TMime"), "TMime is expected to be unlimited.");
- Assert.That(!stationJobs.IsJobUnlimited(station, "TCaptain"), "TCaptain is expected to not be unlimited.");
- Assert.That(!stationJobs.IsJobUnlimited(station, "TClown"), "TClown is expected to not be unlimited.");
- });
- Assert.Multiple(() =>
- {
- Assert.That(stationJobs.TrySetJobSlot(station, "TClown", 0), "Could not set TClown to have zero slots.");
- Assert.That(stationJobs.TryGetJobSlot(station, "TClown", out var clownSlots), "Could not get the number of TClown slots.");
- Assert.That(clownSlots, Is.EqualTo(0));
- Assert.That(!stationJobs.TryAdjustJobSlot(station, "TCaptain", -9999), "Was able to adjust TCaptain by -9999 without clamping.");
- Assert.That(stationJobs.TryAdjustJobSlot(station, "TCaptain", -9999, false, true), "Could not adjust TCaptain by -9999.");
- Assert.That(stationJobs.TryGetJobSlot(station, "TCaptain", out var captainSlots), "Could not get the number of TCaptain slots.");
- Assert.That(captainSlots, Is.EqualTo(0));
- });
- Assert.Multiple(() =>
- {
- Assert.That(stationJobs.TrySetJobSlot(station, "TChaplain", 10, true), "Could not create 10 TChaplain slots.");
- stationJobs.MakeJobUnlimited(station, "TChaplain");
- Assert.That(stationJobs.IsJobUnlimited(station, "TChaplain"), "Could not make TChaplain unlimited.");
- });
- });
- await pair.CleanReturnAsync();
- }
- [Test]
- public async Task InvalidRoundstartJobsTest()
- {
- await using var pair = await PoolManager.GetServerClient();
- var server = pair.Server;
- var prototypeManager = server.ResolveDependency<IPrototypeManager>();
- var compFact = server.ResolveDependency<IComponentFactory>();
- var name = compFact.GetComponentName<StationJobsComponent>();
- await server.WaitAssertion(() =>
- {
- // invalidJobs contains all the jobs which can't be set for preference:
- // i.e. all the jobs that shouldn't be available round-start.
- var invalidJobs = new HashSet<string>();
- foreach (var job in prototypeManager.EnumeratePrototypes<JobPrototype>())
- {
- if (!job.SetPreference)
- invalidJobs.Add(job.ID);
- }
- Assert.Multiple(() =>
- {
- foreach (var gameMap in prototypeManager.EnumeratePrototypes<GameMapPrototype>())
- {
- foreach (var (stationId, station) in gameMap.Stations)
- {
- if (!station.StationComponentOverrides.TryGetComponent(name, out var comp))
- continue;
- foreach (var (job, array) in ((StationJobsComponent) comp).SetupAvailableJobs)
- {
- Assert.That(array.Length, Is.EqualTo(2));
- Assert.That(array[0] is -1 or >= 0);
- Assert.That(array[1] is -1 or >= 0);
- Assert.That(invalidJobs, Does.Not.Contain(job), $"Station {stationId} contains job prototype {job} which cannot be present roundstart.");
- }
- }
- }
- });
- });
- await pair.CleanReturnAsync();
- }
- }
- internal static class JobExtensions
- {
- public static Dictionary<NetUserId, HumanoidCharacterProfile> AddJob(
- this Dictionary<NetUserId, HumanoidCharacterProfile> inp, string jobId, JobPriority prio = JobPriority.Medium,
- int amount = 1)
- {
- for (var i = 0; i < amount; i++)
- {
- inp.Add(new NetUserId(Guid.NewGuid()), HumanoidCharacterProfile.Random().WithJobPriority(jobId, prio));
- }
- return inp;
- }
- public static Dictionary<NetUserId, HumanoidCharacterProfile> AddPreference(
- this Dictionary<NetUserId, HumanoidCharacterProfile> inp, string jobId, JobPriority prio = JobPriority.Medium)
- {
- return inp.ToDictionary(x => x.Key, x => x.Value.WithJobPriority(jobId, prio));
- }
- public static Dictionary<NetUserId, HumanoidCharacterProfile> WithPlayers(
- this Dictionary<NetUserId, HumanoidCharacterProfile> inp,
- Dictionary<NetUserId, HumanoidCharacterProfile> second)
- {
- return new[] { inp, second }.SelectMany(x => x).ToDictionary(x => x.Key, x => x.Value);
- }
- }
|