Arma Reforger Explorer  1.1.0.42
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
SCR_ServicesStatusHelper.c
Go to the documentation of this file.
3 {
4  NOT_EXECUTED = 0, // No backend
5  RUNNING, // Still pinging
6  FINISHED, // Success
7  FAILED // Lost connection
8 }
9 
10 void ScriptInvokerCommStatusMethod(SCR_ECommStatus status, float responseTime, float lastSuccessTime, float lastFailTime);
12 typedef ScriptInvokerBase<ScriptInvokerCommStatusMethod> ScriptInvokerCommStatus;
13 
14 //------------------------------------------------------------------------------------------------
16 {
17  protected static int s_iLastPingUpdate = -1;
18  protected static int s_iLastStatusUpdate = -1;
19  protected static ServiceStatusItem s_MainStatus;
20  protected static const ref array<ServiceStatusItem> SERVICE_STATUSES = {};
21 
22  // Timers
23  protected static const int COMM_STATUS_CHECK_FREQUENCY_MS = 500; // frequency of checking for comm test response
24  protected static const int FIRST_REFRESH_DELAY = 1000; // wait a bit for the very first refresh
25 
26  static const int REFRESH_COOLDOWN = 10000; // minimum ping age to allow for refreshing
27  static const int CONNECTION_CHECK_EXPIRE_TIME = 15000; // fallback in case pinging hangs, considered failed after this time has passed
28  static const int AUTOMATIC_REFRESH_RATE = 60000; // time after which a refresh request is automatically triggered
29 
30  // Statuses
31  static const string STATUS_OK = "ok";
32  static const string STATUS_ERROR = "error";
33 
34  // Services
35  static const string SERVICE_ACCOUNT_PROFILE = "game-identity";
36  static const string SERVICE_XBOX = "game-identity";
37  static const string SERVICE_BI_BACKEND_MULTIPLAYER = "game-api";
38  static const string SERVICE_WORKSHOP = "reforger-workshop-api";
39 
40  // Ping thresholds
41  static const int PING_MAX = 999;
42  static const int PING_THRESHOLD_GOOD = 100;
43  static const int PING_THRESHOLD_BAD = 300;
44  static const int PING_THRESHOLD_AWFUL = 600;
45 
46  protected static SCR_ECommStatus s_eLastReceivedCommStatus = SCR_ECommStatus.RUNNING;
47  protected static bool s_bIsCheckingCommStatus;
48  protected static float s_fLastReceivedCommResponseTime;
49 
50  protected static float s_fPingStartTime;
51 
52  protected static bool s_bFirstRefresh = true;
53 
54  // Invokers
55  protected static ref ScriptInvokerCommStatus s_OnCommStatusCheckFinished;
56  protected static ref ScriptInvokerVoid s_OnCommStatusCheckStart;
57 
58  //------------------------------------------------------------------------------------------------
59  static bool IsBackendEnabled()
60  {
61  return GetGame().GetBackendApi() && GetGame().GetBackendApi().IsRunning();
62  }
63 
64  //------------------------------------------------------------------------------------------------
65  static bool IsBackendReady()
66  {
67  return GetGame().GetBackendApi() && GetGame().GetBackendApi().IsActive();
68  }
69 
70  //------------------------------------------------------------------------------------------------
71  static bool IsBackendInitializing()
72  {
73  return GetGame().GetBackendApi() && GetGame().GetBackendApi().IsInitializing();
74  }
75 
76  //------------------------------------------------------------------------------------------------
77  // PING
78  //------------------------------------------------------------------------------------------------
79  //------------------------------------------------------------------------------------------------
80  protected static bool IsPinging()
81  {
82  return GetGame().GetBackendApi() && GetGame().GetBackendApi().GetCommTestStatus() == 1;
83  }
84 
85  //------------------------------------------------------------------------------------------------
88  // TODO: add a force refresh for when certain important menus are opened? eg we want to disable buttons to open multiplayer or workshop
89  static void RefreshPing()
90  {
91  if (!GetGame() || !GetGame().InPlayMode())
92  return;
93 
94  if (!s_bFirstRefresh && !CanRefresh())
95  {
96  // If we receive a refresh request while in cooldown (for example when a new menu is opened), refresh automatically to update the UI asap
97  if (IsInRefreshCooldown())
98  {
99  ClearRefreshQueue();
100  GetGame().GetCallqueue().CallLater(RefreshPing, (REFRESH_COOLDOWN - GetPingAge()) + 100);
101  }
102 
103  return;
104  }
105 
106  ClearRefreshQueue();
107 
108  if (s_bFirstRefresh)
109  {
110  s_bFirstRefresh = false;
111  s_bIsCheckingCommStatus = true;
112 
113  // TODO: when testing without workbench, immediatly refreshing will cause GetCommTestStatus() to return 0 even if backend is initialized?!
114  GetGame().GetCallqueue().CallLater(RefreshPing_Internal, FIRST_REFRESH_DELAY);
115  return;
116  }
117 
118  RefreshPing_Internal();
119  }
120 
121  //------------------------------------------------------------------------------------------------
122  protected static void RefreshPing_Internal()
123  {
124  // Start pinging to get the current status
125  if (IsBackendReady())
126  GetGame().GetBackendApi().RefreshCommStatus();
127  else
128  s_eLastReceivedCommStatus = SCR_ECommStatus.RUNNING;
129 
130  s_fPingStartTime = System.GetTickCount();
131 
132  StartStatusCheck();
133  }
134 
135  //------------------------------------------------------------------------------------------------
136  protected static void StartStatusCheck()
137  {
138  s_bIsCheckingCommStatus = true;
139 
140  if (s_OnCommStatusCheckStart)
141  s_OnCommStatusCheckStart.Invoke();
142 
143  // This will perform regular checks for backend initialization or communication status, untill a result is obtained or a set amount of time has passed
144  GetGame().GetCallqueue().CallLater(CheckStatus, COMM_STATUS_CHECK_FREQUENCY_MS);
145  }
146 
147  //------------------------------------------------------------------------------------------------
148  protected static void CheckStatus()
149  {
150  s_bIsCheckingCommStatus = false;
151 
152  if (IsBackendReady())
153  CheckCommStatus();
154  else
155  CheckBackendStatus();
156  }
157 
158  //------------------------------------------------------------------------------------------------
159  // Try to give a definitive result
160  // Once a result is out, this is also where the refresh cooldown starts, as it's based on s_iLastPingUpdate
161  protected static void CheckCommStatus()
162  {
163  BackendApi backend = GetGame().GetBackendApi();
164 
165  // Still pinging, need to wait some more to get definitive results
166  if (IsPinging() && !IsPingingStuck())
167  {
168  s_eLastReceivedCommStatus = SCR_ECommStatus.RUNNING;
169 
170  StartStatusCheck();
171  return;
172  }
173 
174  // At this point, the comm test result should be either 0, 2 or 3
175  if (!backend || IsPingingStuck() || !IsBackendReady())
176  s_eLastReceivedCommStatus = SCR_ECommStatus.NOT_EXECUTED;
177  else
178  s_eLastReceivedCommStatus = backend.GetCommTestStatus();
179 
180  // If the comm test went well, update statuses
181  if (s_eLastReceivedCommStatus == SCR_ECommStatus.FINISHED)
182  RefreshStatuses();
183 
184  s_iLastPingUpdate = System.GetTickCount();
185  s_fLastReceivedCommResponseTime = backend.GetCommResponseTime();
186 
187  // Notify that results are out
188  if (s_OnCommStatusCheckFinished)
189  s_OnCommStatusCheckFinished.Invoke(s_eLastReceivedCommStatus, s_fLastReceivedCommResponseTime, backend.GetCommTimeLastSuccess(), backend.GetCommTimeLastFail());
190  }
191 
192  //------------------------------------------------------------------------------------------------
193  // Wait for backend to be ready and then try again
194  protected static void CheckBackendStatus()
195  {
196  // After some time stop checking and move on to giving a result
197  if (!GetGame().GetBackendApi() || IsPingingStuck())
198  {
199  CheckCommStatus();
200  return;
201  }
202 
203  if (IsBackendReady())
204  RefreshPing_Internal();
205  else
206  StartStatusCheck();
207  }
208 
209  //------------------------------------------------------------------------------------------------
210  static void ClearRefreshQueue()
211  {
212  GetGame().GetCallqueue().Remove(RefreshPing);
213  }
214 
215  //------------------------------------------------------------------------------------------------
216  static SCR_ECommStatus GetLastReceivedCommStatus()
217  {
218  return s_eLastReceivedCommStatus;
219  }
220 
221  //------------------------------------------------------------------------------------------------
224  static int GetPingValue()
225  {
226  if (!IsBackendReady() || s_eLastReceivedCommStatus == SCR_ECommStatus.FAILED)
227  return -1;
228 
229  return Math.Round(s_fLastReceivedCommResponseTime);
230  }
231 
232  //------------------------------------------------------------------------------------------------
234  static int GetPingAge()
235  {
236  return System.GetTickCount() - s_iLastPingUpdate;
237  }
238 
239  //------------------------------------------------------------------------------------------------
240  // We want to still allow attempting a refresh if backend is not initialized
241  static bool CanRefresh()
242  {
243  return !s_bIsCheckingCommStatus && !IsInRefreshCooldown();
244  }
245 
246  //------------------------------------------------------------------------------------------------
247  static bool IsInRefreshCooldown()
248  {
249  return GetPingAge() < REFRESH_COOLDOWN;
250  }
251 
252  //------------------------------------------------------------------------------------------------
253  static bool IsPingingStuck()
254  {
255  return System.GetTickCount() - s_fPingStartTime >= CONNECTION_CHECK_EXPIRE_TIME;
256  }
257 
258  //------------------------------------------------------------------------------------------------
259  // STATUSES
260  //------------------------------------------------------------------------------------------------
261  //------------------------------------------------------------------------------------------------
262  static void RefreshStatuses()
263  {
264  if (!IsBackendReady())
265  return;
266 
267  s_MainStatus = GetGame().GetBackendApi().GetMainStatus();
268 
269  SERVICE_STATUSES.Clear();
270  ServiceStatusItem item;
271  for (int i, cnt = GetGame().GetBackendApi().GetStatusCount(); i < cnt; i++)
272  {
273  item = GetGame().GetBackendApi().GetStatusItem(i);
274  if (item)
275  SERVICE_STATUSES.Insert(item);
276  }
277 
278  s_iLastStatusUpdate = System.GetTickCount();
279 
280  #ifdef SERVICES_DEBUG
281  DEBUG();
282  #endif
283  }
284 
285  //------------------------------------------------------------------------------------------------
286  static ServiceStatusItem GetMainStatus()
287  {
288  return s_MainStatus;
289  }
290 
291  //------------------------------------------------------------------------------------------------
294  static ServiceStatusItem GetStatusByName(string statusName)
295  {
296  ServiceStatusItem result;
297 
298  statusName.ToLower();
299  string serviceNameLC;
300 
301  foreach (ServiceStatusItem statusItem : SERVICE_STATUSES)
302  {
303  serviceNameLC = statusItem.Name();
304  serviceNameLC.ToLower();
305 
306  if (serviceNameLC == statusName)
307  return statusItem;
308  }
309 
310  return result;
311  }
312 
313  //------------------------------------------------------------------------------------------------
314  static void GetStatuses(notnull array<ServiceStatusItem> statuses)
315  {
316  statuses.Copy(SERVICE_STATUSES);
317  }
318 
319  //------------------------------------------------------------------------------------------------
320  static bool AreServicesReady()
321  {
322  return s_MainStatus && !SERVICE_STATUSES.IsEmpty();
323  }
324 
325  //------------------------------------------------------------------------------------------------
327  static int GetStatusesAge()
328  {
329  if (!IsBackendReady())
330  return -1;
331 
332  return Math.Round((System.GetTickCount() - s_iLastStatusUpdate) * 0.001);
333  }
334 
335  //------------------------------------------------------------------------------------------------
336  static bool SkipConsoleService(SCR_BackendServiceDisplay service)
337  {
338  return !GetGame().IsPlatformGameConsole() && service.m_bConsoleExclusive;
339  }
340 
341  //------------------------------------------------------------------------------------------------
342  static bool IsServiceActive(string serviceName)
343  {
344  if (s_eLastReceivedCommStatus != SCR_ECommStatus.FINISHED)
345  return false;
346 
347  ServiceStatusItem serviceStatus = GetStatusByName(serviceName);
348  if (!serviceStatus)
349  return false;
350 
351  return serviceStatus.Status() == STATUS_OK;
352  }
353 
354  //------------------------------------------------------------------------------------------------
355  // Check if all conditions for multiplayer are fine in here
356  static bool AreMultiplayerServicesAvailable()
357  {
358  return IsServiceActive(SERVICE_BI_BACKEND_MULTIPLAYER);
359  }
360 
361  //------------------------------------------------------------------------------------------------
362  static bool IsBackendConnectionAvailable()
363  {
364  return s_eLastReceivedCommStatus != SCR_ECommStatus.NOT_EXECUTED && s_eLastReceivedCommStatus != SCR_ECommStatus.RUNNING && IsBackendReady();
365  }
366 
367  //------------------------------------------------------------------------------------------------
368  // INVOKERS
369  //------------------------------------------------------------------------------------------------
370  static ScriptInvokerCommStatus GetOnCommStatusCheckFinished()
371  {
372  if (!s_OnCommStatusCheckFinished)
373  s_OnCommStatusCheckFinished = new ScriptInvokerCommStatus();
374 
375  return s_OnCommStatusCheckFinished;
376  }
377 
378  //------------------------------------------------------------------------------------------------
379  static ScriptInvokerVoid GetOnCommStatusCheckStart()
380  {
381  if (!s_OnCommStatusCheckStart)
382  s_OnCommStatusCheckStart = new ScriptInvokerVoid();
383 
384  return s_OnCommStatusCheckStart;
385  }
386 
387  //------------------------------------------------------------------------------------------------
388  static void DEBUG()
389  {
390  Print("--------------------------------------------------");
391  Print("SERVICE STATUS DEBUG");
392  Print("--------------------------------------------------");
393  if (!GetGame().GetBackendApi())
394  {
395  Print("no backend API found.");
396  return;
397  }
398 
399  Print("is main service found: " + (GetGame().GetBackendApi().GetMainStatus() != null));
400  Print("number of services found: " + GetGame().GetBackendApi().GetStatusCount());
401 
402  if (s_MainStatus)
403  PrintFormat("Service %1: %2 (Name/Status/Message: %3/%4/%5)", -1, s_MainStatus, s_MainStatus.Name(), s_MainStatus.Status(), s_MainStatus.Message());
404  else
405  Print("Main Status not found");
406 
407  foreach (int index, ServiceStatusItem statusItem : SERVICE_STATUSES)
408  {
409  if (statusItem)
410  PrintFormat("Service %1: %2 (Name/Status/Message: %3/%4/%5)", index, statusItem, statusItem.Name(), statusItem.Status(), statusItem.Message());
411  }
412 
413  Print("--------------------------------------------------");
414  }
415 }
416 
417 //------------------------------------------------------------------------------------------------
419 [BaseContainerProps(configRoot : true)]
420 class SCR_BackendServiceDisplayPresets
421 {
422  [Attribute()]
423  protected ref array<ref SCR_BackendServiceDisplay> m_aServices;
424 
425  //------------------------------------------------------------------------------------------------
426  array<ref SCR_BackendServiceDisplay> GetServices()
427  {
428  array<ref SCR_BackendServiceDisplay> services = {};
429  if (m_aServices)
430  services = m_aServices;
431 
432  return services;
433  }
434 }
435 
436 //------------------------------------------------------------------------------------------------
439 {
440  [Attribute(desc: "internal tag")]
441  string m_sId;
442 
443  [Attribute(desc: "the id used to query backend")]
444  string m_sServiceId;
445 
446  [Attribute(desc: "displayed name")]
447  string m_sTitle;
448 
449  [Attribute("0", desc: "set true if this service should be ignored when on PC")]
450  bool m_bConsoleExclusive;
451 };
SCR_ECommStatus
SCR_ECommStatus
This class may become obsolete on BackendAPI update.
Definition: SCR_ServicesStatusHelper.c:2
FAILED
FAILED
Definition: SCR_ServicesStatusHelper.c:6
ScriptInvokerCommStatusMethod
func ScriptInvokerCommStatusMethod
Definition: SCR_ServicesStatusHelper.c:11
GetGame
ArmaReforgerScripted GetGame()
Definition: game.c:1424
BaseContainerProps
class SCR_BackendServiceDisplay BaseContainerProps
desc
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
Definition: SCR_RespawnBriefingComponent.c:17
func
func
Definition: SCR_AIThreatSystem.c:5
ScriptInvokerCommStatus
ScriptInvokerBase< ScriptInvokerCommStatusMethod > ScriptInvokerCommStatus
Definition: SCR_ServicesStatusHelper.c:12
Attribute
typedef Attribute
Post-process effect of scripted camera.
GetServices
int GetServices(out array< SCR_ServicePointComponent > services=null)
Definition: SCR_MilitaryBaseComponent.c:228
SCR_ServicesStatusHelper
Definition: SCR_ServicesStatusHelper.c:15
index
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
Definition: SCR_DestructionSynchronizationComponent.c:17
ScriptInvokerVoid
ScriptInvokerBase< ScriptInvokerVoidMethod > ScriptInvokerVoid
Definition: SCR_ScriptInvokerHelper.c:9
SCR_BaseContainerCustomTitleField
SCR_Faction ScriptedFaction SCR_BaseContainerCustomTitleField("m_sCallsign")
Definition: SCR_Faction.c:672
SCR_BackendServiceDisplay
Definition: SCR_ServicesStatusHelper.c:438
NOT_EXECUTED
NOT_EXECUTED
Definition: SCR_ServicesStatusHelper.c:2
FINISHED
FINISHED
Definition: SCR_ServicesStatusHelper.c:4
RUNNING
RUNNING
Definition: SCR_ServicesStatusHelper.c:3