diff --git a/src/Prospect.Server.Api/Models/Title/ContractInfo.cs b/src/Prospect.Server.Api/Models/Title/ContractInfo.cs index 8417b9e..6e47db2 100644 --- a/src/Prospect.Server.Api/Models/Title/ContractInfo.cs +++ b/src/Prospect.Server.Api/Models/Title/ContractInfo.cs @@ -72,6 +72,21 @@ public enum EYContractObjectiveType { MAX = 8 } +public static class ContractObjectiveTypeExtensions { + // Objectives that can only be observed while a raid is running (kills, dead-drops, + // visiting an area, looting a container). The raid is hosted by the player's own + // client and there is no dedicated game server, so their progress is never reported + // to the backend (UpdatePlayerActiveContracts is never called). They are therefore + // auto-credited server-side, otherwise these contracts can never be completed. + // OwnNumOfItem is excluded (validated against the real stash) and so are the meta + // objectives FactionLevel / CompletedMission (validated from persisted player data). + public static bool IsRaidRuntime(this EYContractObjectiveType type) => + type is EYContractObjectiveType.Kills + or EYContractObjectiveType.DeadDrop + or EYContractObjectiveType.VisitArea + or EYContractObjectiveType.LootContainer; +} + public enum EYContractDifficulty { Invalid = 0, diff --git a/src/Prospect.Server.Api/Services/CloudScript/Functions/ClaimActiveContract.cs b/src/Prospect.Server.Api/Services/CloudScript/Functions/ClaimActiveContract.cs index 5796102..5aa031e 100644 --- a/src/Prospect.Server.Api/Services/CloudScript/Functions/ClaimActiveContract.cs +++ b/src/Prospect.Server.Api/Services/CloudScript/Functions/ClaimActiveContract.cs @@ -131,16 +131,17 @@ public class ClaimActiveContract : ICloudScriptFunction 0) { diff --git a/src/Prospect.Server.Api/Services/CloudScript/Functions/EnterMatchmakingMatch.cs b/src/Prospect.Server.Api/Services/CloudScript/Functions/EnterMatchmakingMatch.cs index 5bf6316..1e2967b 100644 --- a/src/Prospect.Server.Api/Services/CloudScript/Functions/EnterMatchmakingMatch.cs +++ b/src/Prospect.Server.Api/Services/CloudScript/Functions/EnterMatchmakingMatch.cs @@ -62,12 +62,17 @@ public class EnterMatchmakingMatchFunction : ICloudScriptFunction= contractActive.Progress.Length) { + // Defensive: player progress array shorter than the objective list. + break; + } var objective = contract.Objectives[i]; - // Kill objectives: the client-hosted raid has no dedicated game server - // to report per-kill progress to the backend, so kills would never be - // saved. Auto-credit them on deploy so the objective can be completed - // (mirrors the player-kill auto-credit done in ActivateContract). - if (objective.Type == EYContractObjectiveType.Kills) { + // Raid-runtime objectives (kills, dead-drops, visited areas, looted + // containers) are simulated inside the client-hosted raid; with no + // dedicated game server their progress is never reported to the backend, + // so they would never be saved. Auto-credit them on deploy so the + // objective persists and the station shows the contract as completable. + if (objective.Type.IsRaidRuntime()) { contractActive.Progress[i] = objective.MaxProgress; continue; }