Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_DataCollectorCrimesModule.c
Go to the documentation of this file.
3{
4 //------------------------------------------------------------------------------------------------
8 protected ref array<int> m_aPlayerIDs = {};
9
10 //------------------------------------------------------------------------------------------------
14 protected int m_iNextIndex;
15
16 //------------------------------------------------------------------------------------------------
20 [Attribute("3", UIWidgets.Slider, desc: "Seconds after last action to evaluate buffer")]
21 protected float m_fLatestActionThreshold;
22
23 //------------------------------------------------------------------------------------------------
27 [Attribute("5", UIWidgets.Slider, desc: "Max seconds of accumulating actions before evaluating them")]
29
30 //------------------------------------------------------------------------------------------------
34 [Attribute()]
35 protected ref array<int> m_aLightBanPunishments;
36
37 //------------------------------------------------------------------------------------------------
41 [Attribute("1800", UIWidgets.Slider, desc: "Seconds for reincidency of light severity")]
43
44 //------------------------------------------------------------------------------------------------
48 [Attribute()]
49 protected ref array<int> m_aHeavyBanPunishments;
50
51 //------------------------------------------------------------------------------------------------
55 [Attribute("14400", UIWidgets.Slider, desc: "Seconds for reincidency of heavy severity")]
57
58 [Attribute("1", UIWidgets.CheckBox, desc: "Are War Crimes detection enabled?")]
59 protected bool m_bWarCrimesEnabled;
60
61 [Attribute("1", UIWidgets.CheckBox, desc: "Is the proportionality principle enabled?")]
63
64 [Attribute("2", UIWidgets.Slider, desc: "Negative proportionality points for killing a friendly human-controlled soldier")]
66
67 [Attribute("1", UIWidgets.Slider, desc: "Negative proportionality points for killing a friendly AI-controlled soldier")]
69
70 [Attribute("1.5", UIWidgets.Slider, desc: "Negative proportionality points for killing an enemy AI-controlled soldier")]
72
73 [Attribute("3", UIWidgets.Slider, desc: "Positive proportionality points for killing an enemy human-controlled soldier")]
75
76 //~ Used in voting to make sure there is a min ban duration (in seconds)
77 static const int MIN_AUTO_BAN_DURATION = 300;
78
79 //------------------------------------------------------------------------------------------------
80 override void OnPlayerAuditSuccess(int playerID)
81 {
82 super.OnPlayerAuditSuccess(playerID, stuff);
83
84 //If war crimes are not enabled, the module doesn't need to do anything. Keep the array of players empty and just return
86 return;
87
88 m_aPlayerIDs.Insert(playerID);
89
90 //Reset time out of the player in case they were previously disconnected as a punishment
91
92 SCR_PlayerData playerData = GetGame().GetDataCollector().GetPlayerData(playerID, false);
93 if (!playerData)
94 return;
95
96 playerData.SetTimeOut(0);
97 }
98
99 //------------------------------------------------------------------------------------------------
100 override void OnPlayerDisconnected(int playerID, IEntity controlledEntity = null)
101 {
102 super.OnPlayerDisconnected(playerID, controlledEntity);
103
104 int i = m_aPlayerIDs.Find(playerID);
105
106 //if player not found in the array, return
107 if (i == -1)
108 return;
109
110 //Evaluate player no matter what, because they are leaving the server
111 EvaluatePlayerCrimes(playerID, false);
112
113 //Update their acceleration
115
116 //Remove player from the array
117 m_aPlayerIDs.RemoveOrdered(i);
118
119 //Adjust index if necessary
120 if (i < m_iNextIndex)
121 m_iNextIndex--;
122 }
123
124 //------------------------------------------------------------------------------------------------
125 override void OnGameModeEnd()
126 {
127 for (int i = 0; i < m_aPlayerIDs.Count(); i++)
128 {
130 }
131 m_aPlayerIDs.Clear();
132 }
133
134 //------------------------------------------------------------------------------------------------
135 override void Update(float timeTick)
136 {
137 if (m_aPlayerIDs.IsEmpty())
138 return;
139
140 m_fTimeSinceUpdate += timeTick;
141
143 return;
144
145 if (!m_aPlayerIDs.IsIndexValid(m_iNextIndex))
146 {
147 m_iNextIndex = 0;
149 return;
150 }
151
153
154 m_iNextIndex++;
155 }
156
157 //------------------------------------------------------------------------------------------------
158 protected void CheckPlayer(int playerId)
159 {
160 SCR_PlayerData playerData = GetGame().GetDataCollector().GetPlayerData(playerId, false);
161 if (!playerData)
162 return;
163
164 //Are we accumulating actions? If not, that's it for now. Process stats and reset
165 int accumulationTick = playerData.GetAccumulatedActionsTick();
166 if (accumulationTick == 0)
167 {
168 ProcessTemporalStats(playerData);
169 return;
170 }
171
172 //It's been more than m_fLastActionThreshold seconds since last action or more than m_fMaxTimeAccumulationThreshold since started accumulating actions? If not, keep accumulating
173 int currentTick = System.GetTickCount();
174 int latestActionTick = playerData.GetLatestActionTick();
175 if (currentTick - latestActionTick < m_fLatestActionThreshold * 1000 && currentTick - accumulationTick < m_fMaxTimeAccumulationThreshold * 1000)
176 return;
177
178 //Evaluation of war crimes
179 EvaluatePlayerCrimes(playerId);
180 }
181
182 //------------------------------------------------------------------------------------------------
185 protected void EvaluatePlayerCrimes(int playerId, bool evaluatePunishment = true)
186 {
187 SCR_PlayerData playerData = GetGame().GetDataCollector().GetPlayerData(playerId, false);
188 if (!playerData)
189 return;
190
191 //Calculate decreasing of criminal score based on time lapsed
192 float decreasingOfScore = (System.GetTickCount() - playerData.GetLatestCriminalScoreUpdateTick()) * 0.001 * SCR_PlayerDataConfigs.GetInstance().GetScoreDecreasePerSecond();
193
195 // Analsing warcrime_harming_friendlies: //
197
198 float harmingFriendliesScore = EvaluateHarmingFriendlies(playerData) * playerData.GetStat(SCR_EDataStats.CRIME_ACCELERATION);
199
201 // Done analysing war crimes //
203
204 //Process temporal stats
205 ProcessTemporalStats(playerData);
206
207 //Update score
208 UpdateCriminalScore(playerData, harmingFriendliesScore, decreasingOfScore);
209
210 float currentScore = playerData.GetCriminalScore();
211
212 if (harmingFriendliesScore > 0)
213 Print("Player with id " + playerId + " got " + harmingFriendliesScore + " more criminal points. Current criminal score is " + currentScore, LogLevel.DEBUG);
214 else
215 return;
216
217 //Kick or ban players
218 if (evaluatePunishment && currentScore >= SCR_PlayerDataConfigs.GetInstance().GetScoreThreshold())
219 {
220 //Logic for kicking player
221 KickPlayer(playerId, playerData, SCR_PlayerManagerKickReason.FRIENDLY_FIRE);
222 return;
223 }
224
225 //Send educational hints to clients
226 SendHints(playerId, currentScore);
227 }
228
229 //------------------------------------------------------------------------------------------------
230 protected void KickPlayer(int playerId, notnull SCR_PlayerData playerData, SCR_PlayerManagerKickReason reason)
231 {
232 int durationInMinutes = RequestBanIfNecessary(playerId, playerData, reason);
233 if (durationInMinutes == -1)
234 {
235 return;
236 }
237 else
238 {
239 Print("Player Manager is kicking a player with id " + playerId + ". Banning them for " + durationInMinutes + " minutes.", LogLevel.DEBUG);
240 if (durationInMinutes == 0)
242 else
243 ExecutePunishment(playerId, SCR_PlayerManagerKickReason.TEMP_BAN, durationInMinutes * 60);
244 }
245
246 //GetGame().GetPlayerManager().KickPlayer(playerId, reason, durationInMinutes * 60);
247 }
248
249 //------------------------------------------------------------------------------------------------
256 protected int RequestBanIfNecessary(int playerId, notnull SCR_PlayerData playerData, SCR_PlayerManagerKickReason reason)
257 {
258 if (m_aLightBanPunishments.IsEmpty() || m_aHeavyBanPunishments.IsEmpty())
259 {
260 Print("Incorrect setup on DataCollectorCrimesModule: Light or Heavy ban duration array is empty. Cannot ban players!", LogLevel.ERROR);
261 return 0;
262 }
263
264 //Update their session duration to now
265 playerData.CalculateSessionDuration();
266
267 //Set up the necessary fields
268 float acceleration = playerData.GetStat(SCR_EDataStats.CRIME_ACCELERATION);
269 float score = playerData.GetCriminalScore();
270
272 int durationInMinutes;
273
274 //Should this player be banned?
275 //Lightban requires an acceleration of 4.8 or more
276 if (config.GetMaxAcceleration() * config.GetBanEvaluationLight() <= acceleration)
277 {
278 //Yes, they should. Decide between light or heavy ban now.
279
280 //Lightban variables
281 int timeDiffLightBan = playerData.GetStat(SCR_EDataStats.SESSION_DURATION) - playerData.GetStat(SCR_EDataStats.LIGHTBAN_SESSION_DURATION);
282 int streakLightBan = playerData.GetStat(SCR_EDataStats.LIGHTBAN_STREAK);
283 int maxStreakLightBan = m_aLightBanPunishments.Count();
284
285 //Heavyban variables
286 int timeDiffHeavyBan = playerData.GetStat(SCR_EDataStats.SESSION_DURATION) - playerData.GetStat(SCR_EDataStats.HEAVYBAN_SESSION_DURATION);
287 int streakHeavyBan = playerData.GetStat(SCR_EDataStats.HEAVYBAN_STREAK);
288 int maxStreakHeavyBan = m_aHeavyBanPunishments.Count();
289
290 //If It's the first ban issued to this player recently, only light ban is enough.
291 if (timeDiffLightBan > m_iSecondsOfReincidencyLightBan && timeDiffHeavyBan > m_iSecondsOfReincidencyHeavyBan)
292 {
293 durationInMinutes = m_aLightBanPunishments[0];
294 streakLightBan = 1;
295
296 playerData.OverrideStat(SCR_EDataStats.LIGHTBAN_STREAK, streakLightBan);
297 playerData.OverrideStat(SCR_EDataStats.LIGHTBAN_SESSION_DURATION, playerData.GetStat(SCR_EDataStats.SESSION_DURATION));
298 }
299 else
300 //Else: Not a long enough time has passed since the last ban. This means this player has repeated an offense.
301 {
302 //If the streak of lightban is not big enough, or the acceleration is not big enough, lightban it is
303 //Heavyban requires an acceleration of 6 and a lightban streak of at least 3 with current configs
304 if (!(streakLightBan >= maxStreakLightBan && config.GetMaxAcceleration() * config.GetBanEvaluationHeavy() <= acceleration))
305 {
306 //Light ban is enough for this player because the streak and the acceleration don't meet (both of them) the necessary conditions
307
308 durationInMinutes = m_aLightBanPunishments[Math.Min(streakLightBan, maxStreakLightBan - 1)];
309 streakLightBan++;
310
311 playerData.OverrideStat(SCR_EDataStats.LIGHTBAN_STREAK, streakLightBan);
312 playerData.OverrideStat(SCR_EDataStats.LIGHTBAN_SESSION_DURATION, playerData.GetStat(SCR_EDataStats.SESSION_DURATION));
313 }
314 else
315 {
316 //Heavy ban it is
317 if (timeDiffHeavyBan > m_iSecondsOfReincidencyHeavyBan)
318 {
319 durationInMinutes = m_aHeavyBanPunishments[0];
320 streakHeavyBan = 1;
321 playerData.OverrideStat(SCR_EDataStats.HEAVYBAN_STREAK, streakHeavyBan);
322 }
323 else
324 {
325 durationInMinutes = m_aHeavyBanPunishments[Math.Min(streakHeavyBan, maxStreakHeavyBan - 1)];
326 streakHeavyBan++;
327 }
328
329 playerData.OverrideStat(SCR_EDataStats.HEAVYBAN_STREAK, streakHeavyBan);
330 playerData.OverrideStat(SCR_EDataStats.HEAVYBAN_SESSION_DURATION, playerData.GetStat(SCR_EDataStats.SESSION_DURATION));
331
332 BackendApi ba = GetGame().GetBackendApi();
333 if (ba)
334 ExecutePunishment(playerId, SCR_PlayerManagerKickReason.BAN, durationInMinutes * 60);
335 //ba.PlayerBanCreate("Heavy ban", durationInMinutes * 60, playerId);
336
337 Print("Banning service is heavybanning a player with id " + playerId + ". Banning them for " + durationInMinutes + " minutes.", LogLevel.DEBUG);
338 //Player has been banned. We don't want the PlayerManager to ban them on top of the banning service. So we need to set durationInMinutes to an scape value
339 //This implementation should be improved in the future, but for now we can only kick them for 0 minutes using the PlayerManager since its the banning service what banned them.
340 durationInMinutes = -1;
341 }
342 }
343 }
344 //Not a ban. Just a kick
345 else
346 {
347 int timeDiff = playerData.GetStat(SCR_EDataStats.SESSION_DURATION) - playerData.GetStat(SCR_EDataStats.KICK_SESSION_DURATION);
348 int streak = playerData.GetStat(SCR_EDataStats.KICK_STREAK);
349
350 if (timeDiff > 600)
351 streak = 1;
352 else
353 streak ++;
354
355 durationInMinutes = 0;
356
357 playerData.OverrideStat(SCR_EDataStats.KICK_STREAK, streak);
358 playerData.OverrideStat(SCR_EDataStats.KICK_SESSION_DURATION, playerData.GetStat(SCR_EDataStats.SESSION_DURATION));
359 }
360
361 return durationInMinutes;
362 }
363
364 //------------------------------------------------------------------------------------------------
365 protected float EvaluateHarmingFriendlies(notnull SCR_PlayerData playerData)
366 {
367 array<float> accumulatedActions = playerData.GetAccumulatedActions();
368
369 float harmingAlliesPoints = accumulatedActions[SCR_EDataStats.FRIENDLY_KILLS] * MODIFIER_PROPORTIONALITY_FRIENDLY_KILLS + accumulatedActions[SCR_EDataStats.FRIENDLY_AI_KILLS] * MODIFIER_PROPORTIONALITY_FRIENDLY_AI_KILLS;
370 float proportionalityPoints = 0;
371
373 proportionalityPoints = accumulatedActions[SCR_EDataStats.KILLS] * MODIFIER_PROPORTIONALITY_KILLS + accumulatedActions[SCR_EDataStats.AI_KILLS] * MODIFIER_PROPORTIONALITY_AI_KILLS;
374
375 if (harmingAlliesPoints <= proportionalityPoints)
376 return 0;
377
378 playerData.AddStat(SCR_EDataStats.WARCRIME_HARMING_FRIENDLIES, harmingAlliesPoints - proportionalityPoints, false);
379 playerData.AddStat(SCR_EDataStats.WARCRIMES, harmingAlliesPoints - proportionalityPoints, false);
380
381 //Single friendly kill or multiple friendly kills?
382 float PointsOfCrime;
383
384 if (accumulatedActions[SCR_EDataStats.FRIENDLY_KILLS] + accumulatedActions[SCR_EDataStats.FRIENDLY_AI_KILLS] == 1)
386 else
388
389 return PointsOfCrime * (harmingAlliesPoints - proportionalityPoints);
390 }
391
392 //------------------------------------------------------------------------------------------------
393 protected void ExecutePunishment(int playerId, SCR_PlayerManagerKickReason reason, int durationInSeconds)
394 {
395 GetGame().GetDataCollector().GetPlayerData(playerId).SetTimeOut(durationInSeconds);
396
398 VoteForKickOrBan(playerId, reason, durationInSeconds);
399 else
400 KickOrBanPlayer(playerId, reason, durationInSeconds);
401 }
402
403 //------------------------------------------------------------------------------------------------
404 void KickOrBanPlayer(int playerId, SCR_PlayerManagerKickReason reason, int minimmumDuration)
405 {
406 int durationInSeconds = Math.Clamp(GetGame().GetDataCollector().GetPlayerData(playerId).GetTimeOut(), minimmumDuration, int.MAX);
407
408 switch(reason)
409 {
411 {
412 GetGame().GetPlayerManager().KickPlayer(playerId, reason, 0);
413 return;
414 }
415 case SCR_PlayerManagerKickReason.KICK_VOTED:
416 {
417 GetGame().GetPlayerManager().KickPlayer(playerId, reason, durationInSeconds);
418 return;
419 }
420 case SCR_PlayerManagerKickReason.TEMP_BAN:
421 {
422 GetGame().GetPlayerManager().KickPlayer(playerId, reason, durationInSeconds);
423 return;
424 }
426 {
427 GetGame().GetBackendApi().GetBanServiceApi().CreateBanPlayerId(null, playerId, "Heavy ban", durationInSeconds);
428 GetGame().GetPlayerManager().KickPlayer(playerId, reason, 0);
429 return;
430 }
431
432 }
433 }
434
435 //------------------------------------------------------------------------------------------------
443 void VoteForKickOrBan(int playerId, SCR_PlayerManagerKickReason reason, int minimmumDurationBackup)
444 {
445 SCR_VotingManagerComponent votingManager = SCR_VotingManagerComponent.GetInstance();
446 if (!votingManager)
447 KickOrBanPlayer(playerId, reason, minimmumDurationBackup);
448
449 switch(reason)
450 {
452 case SCR_PlayerManagerKickReason.KICK_VOTED:
453 if (!votingManager.StartVoting(EVotingType.AUTO_KICK, 0, playerId))
454 KickOrBanPlayer(playerId, reason, minimmumDurationBackup);
455 return;
456 case SCR_PlayerManagerKickReason.TEMP_BAN:
457 if (!votingManager.StartVoting(EVotingType.AUTO_LIGHTBAN, 0, playerId))
458 KickOrBanPlayer(playerId, reason, minimmumDurationBackup);
459 return;
461 if (!votingManager.StartVoting(EVotingType.AUTO_HEAVYBAN, 0, playerId))
462 KickOrBanPlayer(playerId, reason, minimmumDurationBackup);
463 return;
464 }
465 }
466
467 //------------------------------------------------------------------------------------------------
468 protected void SendHints(int playerId, float harmingFriendliesScore)
469 {
470 if (playerId <= 0)
471 return;
472
473 if (harmingFriendliesScore > 0)
474 {
475 IEntity playerController = GetGame().GetPlayerManager().GetPlayerController(playerId);
476 if (!playerController)
477 return;
478
479 SCR_KickHintComponent hintsComp = SCR_KickHintComponent.Cast(playerController.FindComponent(SCR_KickHintComponent));
480 if (!hintsComp)
481 return;
482
483 hintsComp.NotifyClientCriminalScoreIncreased(SCR_ECrimeNotification.TEAMKILL, harmingFriendliesScore);
484 }
485 }
486
487 //------------------------------------------------------------------------------------------------
488 protected void ProcessTemporalStats(notnull SCR_PlayerData playerData)
489 {
490 array<float> accumulatedActions = playerData.GetAccumulatedActions();
491
492 //Add the stats to the m_aStats of the player since they are not "temporal" anymore
493 for (int i = 0, count = accumulatedActions.Count(); i < count; i++)
494 {
495 float value = accumulatedActions[i];
496 if (value != 0)
497 playerData.AddStat(i, value, false);
498 }
499
500 playerData.ResetAccumulatedActions();
501 }
502
503 //------------------------------------------------------------------------------------------------
504 protected void UpdateCriminalScore(notnull SCR_PlayerData playerData, float addToScore, float decreaseFromScore)
505 {
506 float currentScore = Math.Max(playerData.GetCriminalScore() - decreaseFromScore, 0);
507 currentScore += addToScore;
508
509 playerData.SetCriminalScore(currentScore);
510 }
511
512 //------------------------------------------------------------------------------------------------
513 protected void UpdateCriminalAcceleration(int playerId)
514 {
515 SCR_PlayerData playerData = GetGame().GetDataCollector().GetPlayerData(playerId, false);
516 if (!playerData)
517 return;
518
519 playerData.CalculateSessionDuration();
520 float secondsSession = playerData.GetStat(SCR_EDataStats.SESSION_DURATION) - playerData.GetStat(SCR_EDataStats.SESSION_DURATION, false);
521
522 float decreaseOfAcceleration, increaseOfAcceleration;
523
524 if (secondsSession < 600)
525 decreaseOfAcceleration = 0;
526 else
527 decreaseOfAcceleration = Math.Log10(secondsSession) - 1;
528
530
531 increaseOfAcceleration = playerData.GetCriminalScore() * config.GetScoreToAccelerationMultiplier();
532
533 float currentAcceleration = Math.Max(playerData.GetStat(SCR_EDataStats.CRIME_ACCELERATION) - decreaseOfAcceleration, config.GetMinAcceleration());
534 currentAcceleration = Math.Min(currentAcceleration + increaseOfAcceleration, config.GetMaxAcceleration());
535
536 Print("Player with id " + playerId + " disconnecting. Old acceleration was " + playerData.GetStat(SCR_EDataStats.CRIME_ACCELERATION) + ", new acceleration is " + currentAcceleration, LogLevel.VERBOSE);
537
538 playerData.OverrideStat(SCR_EDataStats.CRIME_ACCELERATION, currentAcceleration);
539 }
540};
EVotingType
Definition EVotingType.c:2
ArmaReforgerScripted GetGame()
Definition game.c:1398
SCR_DataCollectorComponent GetDataCollector()
Definition game.c:110
SCR_AIAnimation_Loitering BaseContainerProps
Commanding menu commanding element class.
SCR_PlayerData GetPlayerData(int playerID, bool createNew=true, bool requestFromBackend=true)
SCR_ECrimeNotification
SCR_EDataStats
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
void SCR_VotingManagerComponent(IEntityComponentSource src, IEntity ent, IEntity parent)
Backend Api instance.
Definition BackendApi.c:14
proto external Managed FindComponent(typename typeName)
Definition Math.c:13
void ExecutePunishment(int playerId, SCR_PlayerManagerKickReason reason, int durationInSeconds)
void UpdateCriminalScore(notnull SCR_PlayerData playerData, float addToScore, float decreaseFromScore)
void ProcessTemporalStats(notnull SCR_PlayerData playerData)
void KickOrBanPlayer(int playerId, SCR_PlayerManagerKickReason reason, int minimmumDuration)
void SendHints(int playerId, float harmingFriendliesScore)
override void OnPlayerAuditSuccess(int playerID)
int RequestBanIfNecessary(int playerId, notnull SCR_PlayerData playerData, SCR_PlayerManagerKickReason reason)
override void OnPlayerDisconnected(int playerID, IEntity controlledEntity=null)
void VoteForKickOrBan(int playerId, SCR_PlayerManagerKickReason reason, int minimmumDurationBackup)
float EvaluateHarmingFriendlies(notnull SCR_PlayerData playerData)
void KickPlayer(int playerId, notnull SCR_PlayerData playerData, SCR_PlayerManagerKickReason reason)
void EvaluatePlayerCrimes(int playerId, bool evaluatePunishment=true)
static SCR_PlayerDataConfigs GetInstance()
int GetLatestCriminalScoreUpdateTick()
int GetAccumulatedActionsTick()
void SetTimeOut(int time)
Called manually from the SCR_DataCollectorCrimesModule.
void CalculateSessionDuration()
float GetStat(SCR_EDataStats stat, bool newStat=true)
static SCR_PlayerData GetPlayerData(int playerID)
float GetCriminalScore()
void OverrideStat(SCR_EDataStats stat, float amount=1)
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
SCR_FieldOfViewSettings Attribute
@ MAX