Skip to content

Conversation

@DrakiaXYZ
Copy link
Contributor

This causes PMCs to initially spawn right at raid "start" when using a simulated raid start time (Scav raids, for example), by offsetting all PMC spawns by the earliest PMC spawn time

It is capped to 1 second at the earliest, because the client silently discards any spawn with a "Time" of 0

@qodo-merge-for-open-source
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
The logic may unintentionally remove PMCs

The current logic first filters PMCs based on the simulated raid start time and
then offsets the remainder. This can result in no PMCs if all spawn early; the
suggestion is to offset all original PMC spawn times first to prevent this.

Examples:

Libraries/SPTarkov.Server.Core/Services/RaidTimeAdjustmentService.cs [142-174]
        mapBase.BossLocationSpawn = mapBase
            .BossLocationSpawn.Where(boss =>
                boss.Time > raidAdjustments.SimulatedRaidStartSeconds // Spawns after simulated player start
                || (
                    !string.Equals(boss.BossName, "pmcusec", StringComparison.OrdinalIgnoreCase) // or
                    && !string.Equals(boss.BossName, "pmcbear", StringComparison.OrdinalIgnoreCase) // isn't a pmc
                )
            )
            .ToList();


 ... (clipped 23 lines)

Solution Walkthrough:

Before:

```csharp
// Filter out PMCs that spawn before the simulated start time
mapBase.BossLocationSpawn = mapBase
    .BossLocationSpawn.Where(boss =>
        boss.Time > raidAdjustments.SimulatedRaidStartSeconds 
        || !IsPmc(boss)
    )
    .ToList();

// Get the *remaining* PMCs
var pmcSpawns = mapBase.BossLocationSpawn.Where(boss => IsPmc(boss));

// If any PMCs remain, offset their spawn times
var firstPmcSpawn = pmcSpawns.OrderBy(boss => boss.Time).FirstOrDefault();
if (firstPmcSpawn != null)
{
    // ... offset logic ...
}



#### After:
```csharp
```csharp
// Separate PMCs from other spawns *before* filtering
var originalPmcSpawns = mapBase.BossLocationSpawn.Where(boss => IsPmc(boss)).ToList();
var otherSpawns = mapBase.BossLocationSpawn.Where(boss => !IsPmc(boss));

// Filter non-PMC spawns based on simulated start time
var filteredOtherSpawns = otherSpawns.Where(boss => boss.Time > raidAdjustments.SimulatedRaidStartSeconds);

// Adjust ALL original PMC spawn times
var firstPmcSpawn = originalPmcSpawns.OrderBy(boss => boss.Time).FirstOrDefault();
if (firstPmcSpawn != null)
{
    var offset = firstPmcSpawn.Time.GetValueOrDefault(1);
    foreach (var spawn in originalPmcSpawns)
    {
        spawn.Time = Math.Max(spawn.Time.GetValueOrDefault(1) - offset, 1);
    }
}

// Recombine the lists
mapBase.BossLocationSpawn = filteredOtherSpawns.Concat(originalPmcSpawns).ToList();




<details><summary>Suggestion importance[1-10]: 9</summary>

__

Why: The suggestion correctly identifies a critical logic flaw where all PMCs could be removed before their spawn times are adjusted, potentially leading to raids with no PMCs, which contradicts the PR's intent.


</details></details></td><td align=center>High

</td></tr><tr><td rowspan=1>General</td>
<td>



<details><summary>Avoid multiple enumeration of IEnumerable</summary>

___

**To avoid multiple enumerations and improve efficiency, convert the <code>pmcSpawns</code> <br><code>IEnumerable</code> to a <code>List</code> by appending <code>.ToList()</code> to the LINQ query.**

[Libraries/SPTarkov.Server.Core/Services/RaidTimeAdjustmentService.cs [162-174]](https://github.com/sp-tarkov/server-csharp/pull/624/files#diff-2f31b6ca8892c0a1dc9aaac00ac5f6f016892cb266c1d9f3beb0b22607b79cacR162-R174)

```diff
-var pmcSpawns = mapBase.BossLocationSpawn.Where(boss => boss.BossName is Sides.PmcUsec or Sides.PmcBear);
-var firstPmcSpawn = pmcSpawns.OrderBy(boss => boss.Time).FirstOrDefault();
+var pmcSpawns = mapBase.BossLocationSpawn.Where(boss => boss.BossName is Sides.PmcUsec or Sides.PmcBear).ToList();
+var firstPmcSpawn = pmcSpawns.MinBy(boss => boss.Time);
 if (firstPmcSpawn != null)
 {
     var pmcStartSeconds = firstPmcSpawn.Time.GetValueOrDefault(1);
     foreach (var spawn in pmcSpawns)
     {
         // Sanity check, the client won't spawn a time of 0
         spawn.Time = (double)Math.Max(spawn.Time.GetValueOrDefault(1) - pmcStartSeconds, 1);
     }
 
     logger.Debug($"Offset PMC spawns by {pmcStartSeconds} seconds");
 }
Suggestion importance[1-10]: 5

__

Why: The suggestion correctly identifies a multiple enumeration issue on an IEnumerable and proposes an efficient fix by materializing the collection with .ToList() and using MinBy, which improves performance and adheres to best practices.

Low
  • More

@chompDev chompDev merged commit 2e71148 into sp-tarkov:develop Oct 16, 2025
5 checks passed
@DrakiaXYZ DrakiaXYZ deleted the fix-scav-pmcspawn branch October 28, 2025 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants