Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_DataCollectorComponent.c
Go to the documentation of this file.
1[EntityEditorProps(category: "GameScripted/DataCollection/", description: "Main component used for collecting player data.")]
5
6class SCR_DataCollectorComponent : SCR_BaseGameModeComponent
7{
8 [Attribute()]
9 protected ref array<ref SCR_DataCollectorModule> m_aModules;
10
11 [Attribute()]
12 protected bool m_bOptionalKicking;
13
14 [Attribute("3", desc: "Optional kicking: Penalty score for killing a friendly player.")]
16
17 [Attribute("1", desc: "Penalty score for killing a friendly AI.")]
19
20 [Attribute("10", desc: "Penalty score limit for a kick from the match.")]
22
23 [Attribute("1800", desc: "Ban duration after a kick (in seconds, -1 for a session-long ban).")]
25
26 [Attribute("900", desc: "How often penalty score subtraction happens (in seconds).")]
28
29 [Attribute("2", desc: "How many penalty points get substracted after each subtraction period.")]
31
33
35
37
38 protected IEntity m_Owner;
39
41
43
44#ifdef ENABLE_DIAG
45 protected bool m_bLocalEntityListening = false;
46 protected int m_iInitializingTimer = 0;
47 protected bool m_bVisualDisplay = false;
48#endif
49
50 //------------------------------------------------------------------------------------------------
51 protected override void OnGameModeEnd(SCR_GameModeEndData data)
52 {
53 foreach (SCR_DataCollectorModule module : m_aModules)
54 {
55 module.OnGameModeEnd();
56 }
57
58 PlayerManager playerManager = GetGame().GetPlayerManager();
59 int playerID;
60 PlayerController playerController;
61 SCR_DataCollectorCommunicationComponent communicationComponent;
62
63 // Here we add to the faction the scores of all the players who haven't disconnected yet
64 SCR_ChimeraCharacter playerChimera;
65 Faction faction;
66
67 for (int i = m_mPlayerData.Count() - 1; i >= 0; i--)
68 {
69 playerID = m_mPlayerData.GetKey(i);
70
71 // We update the duration of the session here because it should not be connected to any module
72 m_mPlayerData.Get(playerID).CalculateSessionDuration();
73
74 playerChimera = SCR_ChimeraCharacter.Cast(playerManager.GetPlayerControlledEntity(playerID));
75 if (!playerChimera)
76 continue;
77
78 faction = playerChimera.GetFaction();
79
80 if (!faction)
81 continue;
82
83 AddStatsToFaction(faction.GetFactionKey(), m_mPlayerData.Get(playerID).CalculateStatsDifference());
84 }
85
86 // We replicate the faction stats now, so they can be found in the client's machine
87 array<FactionKey> factionKeys = {};
88 array<float> factionValues = {};
89 int valuesSize = 0;
90
91 foreach (FactionKey key, array<float> value : m_mFactionScore)
92 {
93 factionKeys.Insert(key);
94 factionValues.InsertAll(value);
95 if (valuesSize == 0)
96 valuesSize = value.Count();
97 }
98
99 for (int i = m_mPlayerData.Count() - 1; i >= 0; i--)
100 {
101 playerID = m_mPlayerData.GetKey(i);
102 playerController = playerManager.GetPlayerController(playerID);
103 if (!playerController)
104 continue;
105
106 communicationComponent = SCR_DataCollectorCommunicationComponent.Cast(playerController.FindComponent(SCR_DataCollectorCommunicationComponent));
107 if (!communicationComponent)
108 continue;
109
110 communicationComponent.SendData(m_mPlayerData.Get(playerID), factionKeys, factionValues, valuesSize);
111 }
112
113 // Get Session data
114 SCR_SessionDataEvent dataEvent = m_SessionData.GetSessionDataEvent();
115
116 dataEvent.name_reason_end = SCR_Enum.GetEnumName(EGameOverTypes, data.GetEndReason());
117
118 FactionManager factionManager = GetGame().GetFactionManager();
119 if (factionManager)
120 {
121 Faction winFaction = factionManager.GetFactionByIndex(data.GetWinnerFactionId());
122 if (winFaction)
123 dataEvent.name_winner_faction = winFaction.GetFactionKey();
124 }
125 }
126
127 //------------------------------------------------------------------------------------------------
133 array<float> GetFactionStats(FactionKey key)
134 {
135 return m_mFactionScore.Get(key);
136 }
137
138 //------------------------------------------------------------------------------------------------
148
149 //------------------------------------------------------------------------------------------------
153 void AddStatsToFaction(FactionKey key, notnull array<float> stats)
154 {
155 array<float> factionStats = m_mFactionScore.Get(key);
156 //If the faction doesn't exist in our map yet, just create it and initialize it with these stats
157 if (!factionStats)
158 {
159 m_mFactionScore.Insert(key, stats);
160 }
161 else
162 {
163 int statsCount = stats.Count();
164
165 if (statsCount == 0 || factionStats.Count() != statsCount)
166 {
167 Print("ERROR WHEN ADDING FACTIONSTATS IN DATA COLLECTOR: Size of faction stats is different than expected size! Expected size was" + statsCount + " but real size was "+ factionStats.Count(), LogLevel.WARNING);
168 return;
169 }
170
171 for (int i = 0; i < statsCount; i++)
172 {
173 factionStats[i] = factionStats[i] + stats[i];
174 }
175 }
176 }
177
178 //------------------------------------------------------------------------------------------------
180 override void OnGameEnd()
181 {
182 for (int i = m_mPlayerData.Count() - 1; i >= 0; i--)
183 {
184 SCR_PlayerData playerData = GetPlayerData(m_mPlayerData.GetKey(i), false);
185 if (playerData)
186 playerData.StoreProfile();
187 }
188 }
189
190 //------------------------------------------------------------------------------------------------
195 {
196 for (int i = m_aModules.Count() - 1; i >= 0; i--)
197 {
198 if (m_aModules[i].Type() == type)
199 return m_aModules[i];
200 }
201 return null;
202 }
203
204 //------------------------------------------------------------------------------------------------
205 protected bool IsMaster()
206 {
207 RplComponent rplComponent = RplComponent.Cast(GetOwner().FindComponent(RplComponent));
208 return (rplComponent && rplComponent.IsMaster());
209 }
210
211 //------------------------------------------------------------------------------------------------
213 {
214 if (!m_SessionData)
215 return null;
216
217 return m_SessionData.GetDataEventStats();
218 }
219
220 //------------------------------------------------------------------------------------------------
223 Managed GetPlayerDataStats(int playerID)
224 {
225 SCR_PlayerData playerData = GetPlayerData(playerID);
226
227 return playerData.GetDataEventStats();
228 }
229
230 //------------------------------------------------------------------------------------------------
232 void RemovePlayer(int playerID)
233 {
234 m_mPlayerData.Remove(playerID);
235 }
236
237 //------------------------------------------------------------------------------------------------
242 SCR_PlayerData GetPlayerData(int playerID, bool createNew = true, bool requestFromBackend = true)
243 {
244 SCR_PlayerData playerData = m_mPlayerData.Get(playerID);
245 if (!playerData && createNew)
246 {
247 playerData = new SCR_PlayerData(playerID, true, requestFromBackend);
248 m_mPlayerData.Insert(playerID, playerData);
249 }
250
251 return playerData;
252 }
253
254 //------------------------------------------------------------------------------------------------
255 protected int GetPlayers(out notnull array<int> outPlayers)
256 {
257 if (m_mPlayerData.IsEmpty())
258 return 0;
259
260 for (int i = m_mPlayerData.Count() - 1; i >= 0; i--)
261 {
262 outPlayers.Insert(m_mPlayerData.GetKey(i));
263 }
264
265 return m_mPlayerData.Count();
266 }
267
268 //------------------------------------------------------------------------------------------------
273
274 //------------------------------------------------------------------------------------------------
275 // OnAuditSuccess is the moment when the player has not only connected, but also been authenticated
276 protected override void OnPlayerAuditSuccess(int playerId)
277 {
278 Print("Player with id " + playerId + " was auditted succesfully and admitted on the Data Collector", LogLevel.DEBUG);
279 //We create the player's PlayerData here
280 GetPlayerData(playerId);
281
282 //And then let the modules handle the newly connected player if they need to
283 foreach (SCR_DataCollectorModule module : m_aModules)
284 {
285 module.OnPlayerAuditSuccess(playerId);
286 }
287 }
288
289 //------------------------------------------------------------------------------------------------
290 protected override void OnPlayerConnected(int playerId)
291 {
293 m_OptionalKicking.OnPlayerConnected(playerId);
294 }
295
296 //------------------------------------------------------------------------------------------------
297 protected override void OnPlayerDisconnected(int playerId, KickCauseCode cause, int timeout)
298 {
299 SCR_PlayerData playerDisconnectedData = GetPlayerData(playerId);
300 foreach (SCR_DataCollectorModule module : m_aModules)
301 {
302 module.OnPlayerDisconnected(playerId);
303 }
304
305 playerDisconnectedData.StoreProfile();
306
307 // ADD STATS TO FACTION
308 // Here we add the stats of the individual player who desconnected to the faction
309 // We only do that if the game is not in POSTGAME state, because if it is we already added this player's stats to the faction in the OnGameModeEnd method
310
312 if (gameMode.GetState() != SCR_EGameModeState.POSTGAME)
313 {
314 IEntity player = GetGame().GetPlayerManager().GetPlayerControlledEntity(playerId);
315 SCR_ChimeraCharacter playerChimera = SCR_ChimeraCharacter.Cast(player);
316 if (playerChimera)
317 {
318 Faction faction = playerChimera.GetFaction();
319 if (faction)
320 AddStatsToFaction(faction.GetFactionKey(), playerDisconnectedData.CalculateStatsDifference());
321 }
322 }
323
324 // DONE ADDING STATS TO THE FACTION
325 //We cannot remove this instance of data from the player collector because the event has not been sent yet to the Database for tracking purposes
326 //m_mPlayerData.Remove(playerId);
327 }
328
329 //------------------------------------------------------------------------------------------------
330 protected override void OnPlayerSpawnFinalize_S(SCR_SpawnRequestComponent requestComponent, SCR_SpawnHandlerComponent handlerComponent, SCR_SpawnData data, IEntity entity)
331 {
332 int playerId = requestComponent.GetPlayerId();
333 foreach (SCR_DataCollectorModule module : m_aModules)
334 {
335 module.OnPlayerSpawned(playerId, entity);
336 }
337 }
338
339 //------------------------------------------------------------------------------------------------
340 protected override void OnPlayerKilled(notnull SCR_InstigatorContextData instigatorContextData)
341 {
342 int playerId = instigatorContextData.GetVictimPlayerID();
343 IEntity playerEntity = instigatorContextData.GetVictimEntity();
344 IEntity killerEntity = instigatorContextData.GetKillerEntity();
345 Instigator instigator = instigatorContextData.GetInstigator();
346
347 foreach (SCR_DataCollectorModule module : m_aModules)
348 {
349 module.OnPlayerKilled(playerId, playerEntity, killerEntity, instigator, instigatorContextData);
350 }
351
353 m_OptionalKicking.OnControllableDestroyed(playerEntity, killerEntity, instigator, instigatorContextData);
354 }
355
356 //------------------------------------------------------------------------------------------------
357 protected void OnAIKilled(IEntity AIEntity, IEntity killerEntity, notnull Instigator instigator, notnull SCR_InstigatorContextData instigatorContextData)
358 {
359 foreach (SCR_DataCollectorModule module : m_aModules)
360 {
361 module.OnAIKilled(AIEntity, killerEntity, instigator, instigatorContextData);
362 }
363
365 m_OptionalKicking.OnControllableDestroyed(AIEntity, killerEntity, instigator, instigatorContextData);
366 }
367
368 //------------------------------------------------------------------------------------------------
369 // This method is a hack to process killings when the dead entity is an AI
370 // Because there is no "OnAiKilled" method
371 protected override void OnControllableDestroyed(notnull SCR_InstigatorContextData instigatorContextData)
372 {
373 IEntity entity = instigatorContextData.GetVictimEntity();
374
375 if (!SCR_ChimeraCharacter.Cast(entity))
376 {
377 // Spoiler - it was a vehicle, not an error
378 // Print("Error: The OnControllableDestroyed method from the Data Collector was invoked with an IEntity that is not a chimera character."
379 // + "Dead entity is" + entity +", and killerEntity is "+killerEntity, LogLevel.ERROR);
380 // PrintFormat("Dead: %1", entity);
381 // PrintFormat("KillerEntity: %1", killerEntity);
382 return;
383 }
384
385 //If playerId is not 0 it means that the entity killed was a player
386 //Therefore it will be handled by the OnPlayerKilled event
387 //so we don't need to do anything else
388 if (instigatorContextData.GetVictimPlayerID() > 0)
389 return;
390
391 OnAIKilled(entity, instigatorContextData.GetKillerEntity(), instigatorContextData.GetInstigator(), instigatorContextData);
392 }
393
394#ifdef ENABLE_DIAG
395 //------------------------------------------------------------------------------------------------
398 void OnPlayerEntityChanged(IEntity from, IEntity to)
399 {
400 foreach (SCR_DataCollectorModule module : m_aModules)
401 {
402 module.OnControlledEntityChanged(from, to);
403 }
404 }
405
406 //------------------------------------------------------------------------------------------------
408 protected void ListenToLocalControllerEntityChanged()
409 {
410 SCR_PlayerController playerController = SCR_PlayerController.Cast(GetGame().GetPlayerController());
411
412 if (playerController)
413 {
414 m_bLocalEntityListening = true;
415 playerController.m_OnControlledEntityChanged.Insert(OnPlayerEntityChanged);
416 }
417 }
418
419 //------------------------------------------------------------------------------------------------
420 // Prototyping method. ENABLE_DIAG CLI or #define required
421 protected void CreateStatVisualizations()
422 {
423 if (!m_UiHandler)
424 m_UiHandler = new SCR_DataCollectorUI();
425
426 for (int i = m_aModules.Count() - 1; i >= 0; i--)
427 {
428 m_aModules[i].CreateVisualization();
429 }
430 }
431
432 //------------------------------------------------------------------------------------------------
434 SCR_DataCollectorUI GetUIHandler()
435 {
436 return m_UiHandler;
437 }
438#endif
439
440 //------------------------------------------------------------------------------------------------
441 protected override void OnGameModeStart()
442 {
445 }
446
447 //------------------------------------------------------------------------------------------------
449 protected void DisableModules()
450 {
451 m_aModules.Clear();
452 }
453
454 //------------------------------------------------------------------------------------------------
459 {
460 //If it's no server, disable all tracking
461 if (!IsMaster())
462 {
463#ifndef ENABLE_DIAG
465 m_bOptionalKicking = false;
466 return;
467#endif
468 }
469
470 //Init all modules
471 foreach (SCR_DataCollectorModule module : m_aModules)
472 {
473 module.InitModule();
474 }
475
476 bool writingRights = false;
477 BackendApi ba = GetGame().GetBackendApi();
478
479 if (ba)
480 {
481 SessionStorage baStorage = ba.GetStorage();
482 if (baStorage)
483 writingRights = baStorage.GetOnlineWritePrivilege();
484 }
485
486 //Local storage or online backend storage?
487 if (!writingRights)
488 Print("DataCollectorComponent: StartDataCollectorSession: This server has no writing privileges. Will use local storage instead.", LogLevel.DEBUG);
489 else
490 Print("DataCollectorComponent: StartDataCollectorSession: Using online backend storage.", LogLevel.DEBUG);
491
492 if (!m_Owner)
493 Print("DataCollectorComponent: StartDataCollectorSession: m_Owner is null. Can't add the EntityEvent.FRAME flag thus data collector will not work properly.", LogLevel.ERROR);
494 else
495 SetEventMask(m_Owner, EntityEvent.FRAME); //Activate the FRAME flag
496 }
497
498 //------------------------------------------------------------------------------------------------
499 protected override void EOnFrame(IEntity owner, float timeSlice)
500 {
501#ifdef ENABLE_DIAG
502
503 // Hotfix: AND I AM DISABLING THIS, because it causes other invokers to be duplicit after respawn
504 // TODO: Make something more serious
505 /*
506 //I am doing this because the OnAudit invoker is only used on server-side
507 //Here we want to be able to debug the tracking of career stuff on client-side
508 //since there's no event for it, we need to check periodically until
509 //a local player controller is found
510 if (!m_bLocalEntityListening)
511 {
512 if (m_iInitializingTimer % 100)
513 {
514 ListenToLocalControllerEntityChanged();
515 }
516 m_iInitializingTimer++;
517 }
518 */
519
520 if (m_bVisualDisplay != DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_DATA_COLLECTION_ENABLE_DIAG))
521 {
522 if (!m_UiHandler)
523 CreateStatVisualizations();
524
525 m_bVisualDisplay = DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_DATA_COLLECTION_ENABLE_DIAG);
526 m_UiHandler.SetVisible(m_bVisualDisplay);
527 }
528#endif
529
530 for (int i = m_aModules.Count() - 1; i >= 0; i--)
531 {
532 m_aModules[i].Update(timeSlice);
533 }
534 }
535
536 //------------------------------------------------------------------------------------------------
537 protected override void OnPostInit(IEntity owner)
538 {
539 //If there is a data collector instance already, return
541 return;
542
544
545 //Register the data collector
546 GetGame().RegisterDataCollector(this);
547
548 m_Owner = owner;
549 }
550}
SCR_DebugMenuID
This enum contains all IDs for DiagMenu entries added in script.
Definition DebugMenuID.c:4
EGameOverTypes
ArmaReforgerScripted GetGame()
Definition game.c:1398
SCR_DataCollectorComponent GetDataCollector()
Definition game.c:110
override void OnGameModeEnd(SCR_GameModeEndData data)
override void OnGameEnd()
Called on all machines when the world ends.
SCR_AnalyticsDataCollectionModule FindModule(typename type)
SCR_BaseGameMode GetGameMode()
void SCR_BaseGameModeComponent(IEntityComponentSource src, IEntity ent, IEntity parent)
override void OnGameModeStart()
void OnPlayerKilled(notnull SCR_InstigatorContextData instigatorContextData)
enum SCR_ECompassType EntityEditorProps(category:"GameScripted/Gadgets", description:"Compass", color:"0 0 255 255")
Prefab data class for compass component.
Managed GetPlayerDataStats(int playerID)
void DisableModules()
Use this method to disable all the modules.
void OnAIKilled(IEntity AIEntity, IEntity killerEntity, notnull Instigator instigator, notnull SCR_InstigatorContextData instigatorContextData)
void StartDataCollectorSession()
ref SCR_SessionData m_SessionData
int m_iOptionalKickingPenaltySubtractionPeriod
SCR_SessionData GetSessionData()
int GetPlayers(out notnull array< int > outPlayers)
int m_iOptionalKickingFriendlyAIKillPenalty
int m_iOptionalKickingBanDuration
int m_iOptionalKickingPenaltySubtractionPoints
ref SCR_LocalPlayerPenalty m_OptionalKicking
SCR_PlayerData GetPlayerData(int playerID, bool createNew=true, bool requestFromBackend=true)
ref map< int, ref SCR_PlayerData > m_mPlayerData
ref map< FactionKey, ref array< float > > m_mFactionScore
ref SCR_DataCollectorUI m_UiHandler
void RemovePlayer(int playerID)
map< FactionKey, ref array< float > > GetAllFactionStats()
int m_iOptionalKickingFriendlyPlayerKillPenalty
bool m_bOptionalKicking
array< float > GetFactionStats(FactionKey key)
void AddStatsToFaction(FactionKey key, notnull array< float > stats)
Managed GetSessionDataStats()
override void OnPlayerAuditSuccess(int playerId)
int m_iOptionalKickingKickPenaltyLimit
EDamageType type
SCR_EGameModeState
Get all prefabs that have the spawner data
void OnControllableDestroyed(IEntity entity, IEntity killerEntity, Instigator instigator, notnull SCR_InstigatorContextData instigatorContextData)
void SCR_LocalPlayerPenalty(int friendlyPlayerKillPenalty, int friendlyAIKillPenalty, int penaltyLimit, int banDuration, int penaltySubtractionPeriod, int penaltySubtractionPoints)
override void EOnFrame(IEntity owner, float timeSlice)
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
override void OnPlayerSpawnFinalize_S(SCR_SpawnRequestComponent requestComponent, SCR_SpawnHandlerComponent handlerComponent, SCR_SpawnData data, IEntity entity)
int Type
enum EVehicleType IEntity
Backend Api instance.
Definition BackendApi.c:14
Diagnostic and developer menu system.
Definition DiagMenu.c:18
SCR_EGameModeState GetState()
ref OnControlledEntityChangedPlayerControllerInvoker m_OnControlledEntityChanged
sealed array< float > CalculateStatsDifference()
sealed void StoreProfile()
Managed GetDataEventStats()
Save & Load handler.
Definition Types.c:486
IEntity GetOwner()
Owner entity of the fuel tank.
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
EntityEvent
Various entity events.
Definition EntityEvent.c:14
override void OnPlayerDisconnected(int playerId, KickCauseCode cause, int timeout)
proto external PlayerController GetPlayerController()
void OnPlayerConnected(int playerId)