Arma Reforger Explorer  1.1.0.42
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
SCR_AddonManager.c
Go to the documentation of this file.
1 /*
2 Several classes for addon manager.
3 
4 SCR_WorkshopItem
5 
6 Wraps functionality of WorkshopItem and Dependency objects. The main purpose of this class is to
7 provide access to WorkshopItem from multiple menus at the same time. For everything to work correctly, perform all
8 actions with workshop items through this class, instead of accessing the WorkshopItem methods direcly.
9 
10 To create a SCR_WorkshopItem object, use SCR_AddonManager.Register() methods.
11 
12 Make sure you use strong refs (ref keyword) to store pointers to SCR_WorkshopItem object, because it might get garbage
13 collected by SCR_AddonManager if it is not needed by any other object.
14 
15 
16 SCR_AddonManager
17 
18 Centralized storage of all SCR_WorkshopItem objects. It performs registration and updates of SCR_WorkshopItem objects.
19 
20 It is an Entity and must be placed in the world to use it.
21 
22 
23 SCR_WorkshopItemAction
24 
25 Base class for asynchronous operations of workshop items. Some methods of SCR_WorkshopItem return an action object,
26 which you can use to monitor the result of the operation, cancel/pause/resume it, or subscribe to its events. It's also the only way to know
27 if the action was canceled by something else.
28 
29 
30 !!! Methods marked with "Internal_" are not meant to be used publicly, although they are declared public.
31 */
32 
33 // Enum of bit flags used for doing queries
35 {
36  ONLINE = 1<<0,
37  NOT_ONLINE = 1<<1,
38 
39  OFFLINE = 1<<2,
40  NOT_OFFLINE = 1<<3,
41 
42  ENABLED = 1<<4,
43  NOT_ENABLED = 1<<5,
44 
45  BLOCKED = 1<<6,
47  RESTRICTED = 1<<8,
48 
51 
53 
55 
56  NO_PROBLEMS = 1<<13,
57 
61 
62  FAVOURITE = 1<<17,
63 
65 };
66 
67 [EntityEditorProps(category: "", description: "A centralized system which lets many users perform actions on addons. Most likely only needed in the main menu world.")]
68 class SCR_AddonManagerClass: GenericEntityClass
69 {
70 };
71 
73 {
74  // friend class SCR_WorkshopItem
75 
76  // Constants
77  static const string ADDONS_CLI = "addons"; // Cli parameter used to load addons.
78  static const string VERSION_DOT = ".";
79  protected const static float ADDONS_ENABLED_UPDATE_INTERVAL_S = 1/30;
80  protected const static float ADDONS_OUTDATED_UPDATE_INTERVAL_S = 1.0;
81 
82  // Public callbacks
83  ref ScriptInvoker m_OnAddonsChecked = new ScriptInvoker;
84 
85  // Called when a new addon is downloaded or uninstalled
86  ref ScriptInvoker m_OnAddonOfflineStateChanged = new ScriptInvoker; // (SCR_WorkshopItem item, bool newState) - newState: true - addon downloaded, false - addon deleted
87 
88  // Called when reported state of addon is changed
89  ref ScriptInvoker m_OnAddonReportedStateChanged = new ScriptInvoker; // (SCR_WorkshopItem item, bool newReported) - newReported: new reported value
90 
91  // Called as a result of NegotiateUgcPrivilege
92  ref ScriptInvoker m_OnUgcPrivilegeResult = new ScriptInvoker; // (bool result)
93 
94  // Called wherever set of enabled addons has changed
95  ref ScriptInvoker m_OnAddonsEnabledChanged = new ScriptInvoker; //
96 
97 
98  // Other
99  protected ref array<WorkshopItem> m_aAddonsToRegister = {};
100  protected ref map<string, ref SCR_WorkshopItem> m_mItems = new map<string, ref SCR_WorkshopItem>();
101  protected static SCR_AddonManager s_Instance; // Pointer to instance of this class
102  protected ref SCR_WorkshopAddonManagerPresetStorage m_Storage;
103  protected int m_iAddonsOutdated;
104  protected float m_fAddonsOutdatedTimer = 0;
105  protected float m_fAddonsEnabledTimer = 0;
106  protected string m_sAddonsEnabledPrev;
107 
108  // Connected to WorkshopApi.OnItemsChecked, which automatically runs at start up.
109  protected bool m_bAddonsChecked = false;
110  protected ref SCR_WorkshopCallbackBase m_CallbackCheckAddons;
111  protected ref SCR_ScriptPlatformRequestCallback m_CallbackGetPrivilege;
112  protected bool m_bInitFinished = false;
113 
114  protected ref SCR_BackendCallback m_AddonCheckCallback = new SCR_BackendCallback();
115 
116  //-----------------------------------------------------------------------------------------------
117  // P U B L I C A P I
118  //-----------------------------------------------------------------------------------------------
119 
120  // Called when a new download have been started
121  ref ScriptInvoker m_OnNewDownload = new ScriptInvoker; // (SCR_WorkshopItem item, SCR_WorkshopItemActionDownload action)
122 
123  //-----------------------------------------------------------------------------------------------
124  static SCR_AddonManager GetInstance()
125  {
126  return s_Instance;
127  }
128 
129 
130  //-----------------------------------------------------------------------------------------------
132  array<ref SCR_WorkshopItem> GetAllAddons()
133  {
134  array<ref SCR_WorkshopItem> ret = {};
135  foreach (string id, SCR_WorkshopItem item : m_mItems)
136  {
137  ret.Insert(item);
138  }
139  return ret;
140  }
141 
142  //-----------------------------------------------------------------------------------------------
144  array<ref SCR_WorkshopItem> GetOfflineAddons()
145  {
146  array<ref SCR_WorkshopItem> ret = {};
147  foreach (string id, SCR_WorkshopItem item : m_mItems)
148  {
149  if (item.GetOffline())
150  ret.Insert(item);
151  }
152  return ret;
153  }
154 
155 
156  //-----------------------------------------------------------------------------------------------
158  SCR_WorkshopItem Register(WorkshopItem item)
159  {
160  string id = GetItemId(item);
161  SCR_WorkshopItem existingItem = m_mItems.Get(id);
162 
163  #ifdef WORKSHOP_DEBUG
164  _print(string.Format("Register WorkshopItem: ID: %1, Name: %2, Found in map: %3", id, item.Name(), existingItem != null));
165  #endif
166 
167  if (existingItem)
168  {
169  existingItem.Internal_UpdateObjects(item, null);
170 
171  #ifdef WORKSHOP_DEBUG
172  //existingItem.LogState();
173  #endif
174 
175  return existingItem;
176  }
177 
178  SCR_WorkshopItem newItem = SCR_WorkshopItem.Internal_CreateFromWorkshopItem(item);
179  RegisterNewItem(id, newItem);
180 
181  #ifdef WORKSHOP_DEBUG
182  newItem.LogState();
183  #endif
184 
185  return newItem;
186  }
187 
188 
189  //-----------------------------------------------------------------------------------------------
191  SCR_WorkshopItem Register(Dependency item)
192  {
193  string id = GetItemId(item);
194  SCR_WorkshopItem existingItem = m_mItems.Get(id);
195 
196  #ifdef WORKSHOP_DEBUG
197  _print(string.Format("Register Dependency: ID: %1, Name: %2, Found in map: %3", id, item.GetName(), existingItem != null));
198  #endif
199 
200  if (existingItem)
201  {
202  existingItem.Internal_UpdateObjects(null, item);
203 
204  #ifdef WORKSHOP_DEBUG
205  //existingItem.LogState();
206  #endif
207 
208  return existingItem;
209  }
210 
211  SCR_WorkshopItem newItem = SCR_WorkshopItem.Internal_CreateFromDependency(item);
212  RegisterNewItem(id, newItem);
213 
214  #ifdef WORKSHOP_DEBUG
215  newItem.LogState();
216  #endif
217 
218  return newItem;
219  }
220 
221 
222  //-----------------------------------------------------------------------------------------------
225  bool GetReady() { return m_bInitFinished && m_bAddonsChecked; }
226 
227  //-----------------------------------------------------------------------------------------------
229  //bool GetAllAsyncChecksDone() { return m_bAddonsChecked ; }
230 
231  //-----------------------------------------------------------------------------------------------
232  bool GetAddonsChecked() { return m_bAddonsChecked; }
233 
234  //-----------------------------------------------------------------------------------------------
236  bool GetUgcPrivilege()
237  {
238  return GetGame().GetPlatformService().GetPrivilege(UserPrivilege.USER_GEN_CONTENT);
239  }
240 
241  //----------------------------------------------------------------------------------------------------------------------------------------------------
243  int GetCountAddonsOutdated()
244  {
245  return m_iAddonsOutdated;
246  }
247 
248  //-----------------------------------------------------------------------------------------------
252  void NegotiateUgcPrivilegeAsync()
253  {
254  _print("CheckPrivilege()", LogLevel.NORMAL);
255 
256  if (!m_CallbackGetPrivilege)
257  {
258  m_CallbackGetPrivilege = new SCR_ScriptPlatformRequestCallback();
259  m_CallbackGetPrivilege.m_OnResult.Insert(Callback_GetPrivilege_OnPrivilegeResult);
260  }
261 
262  GetGame().GetPlatformService().GetPrivilegeAsync(UserPrivilege.USER_GEN_CONTENT, m_CallbackGetPrivilege);
263  }
264 
265 
266  // Handling of addons loaded through external configuration
267 
268  //-----------------------------------------------------------------------------------------------
270  protected static bool GetAddonsEnabledExternally()
271  {
272  return System.IsCLIParam(ADDONS_CLI);
273  }
274 
275  //-----------------------------------------------------------------------------------------------
277  static bool GetAddonEnabledExternally(SCR_WorkshopItem item)
278  {
279  if (!GetAddonsEnabledExternally())
280  return false;
281 
282  return item.GetLoaded();
283  }
284 
285  //-----------------------------------------------------------------------------------------------
288  static array<ref SCR_WorkshopItem> SelectItemsBasic(array<ref SCR_WorkshopItem> items, EWorkshopItemQuery query)
289  {
290  array<ref SCR_WorkshopItem> ret = {};
291  foreach (SCR_WorkshopItem item : items)
292  {
293  if (CheckQueryFlag(item, query))
294  ret.Insert(item);
295  }
296 
297  return ret;
298  }
299 
300  //-----------------------------------------------------------------------------------------------
303  static int CountItemsBasic(array<ref SCR_WorkshopItem> items, EWorkshopItemQuery query, bool returnOnFirstMatch = false)
304  {
305  int count = 0;
306  foreach (SCR_WorkshopItem item : items)
307  {
308  if (CheckQueryFlag(item, query))
309  {
310  count++;
311  if (returnOnFirstMatch)
312  return count;
313  }
314  }
315 
316  return count;
317  }
318 
319 
320  //-----------------------------------------------------------------------------------------------
323  static array<ref SCR_WorkshopItem> SelectItemsAnd(array<ref SCR_WorkshopItem> items, EWorkshopItemQuery query)
324  {
325  array<ref SCR_WorkshopItem> ret = {};
326  foreach (SCR_WorkshopItem item : items)
327  {
328  if (CheckQueryFlagsAnd(item, query))
329  ret.Insert(item);
330  }
331 
332  return ret;
333  }
334 
335 
336  //-----------------------------------------------------------------------------------------------
338  static int CountItemsAnd(array<ref SCR_WorkshopItem> items, EWorkshopItemQuery query, bool returnOnFirstMatch = false)
339  {
340  int count = 0;
341  foreach (SCR_WorkshopItem item : items)
342  {
343  if (CheckQueryFlagsAnd(item, query))
344  {
345  count++;
346  if (returnOnFirstMatch)
347  return count;
348  }
349  }
350 
351  return count;
352  }
353 
354 
355  //-----------------------------------------------------------------------------------------------
358  static array<ref SCR_WorkshopItem> SelectItemsOr(array<ref SCR_WorkshopItem> items, EWorkshopItemQuery query)
359  {
360  array<ref SCR_WorkshopItem> ret = {};
361  foreach (SCR_WorkshopItem item : items)
362  {
363  if (CheckQueryFlagsOr(item, query))
364  ret.Insert(item);
365  }
366 
367  return ret;
368  }
369 
370 
371  //-----------------------------------------------------------------------------------------------
373  static int CountItemsOr(array<ref SCR_WorkshopItem> items, EWorkshopItemQuery query, bool returnOnFirstMatch = false)
374  {
375  int count = 0;
376  foreach (SCR_WorkshopItem item : items)
377  {
378  if (CheckQueryFlagsOr(item, query))
379  {
380  count++;
381  if (returnOnFirstMatch)
382  return count;
383  }
384  }
385 
386  return count;
387  }
388 
389 
390  //-----------------------------------------------------------------------------------------------
392  //OBSOLETE => use Revision.CompareTo
393  static SCR_ComparerOperator DifferenceBetweenVersions(string vFrom, string vTo)
394  {
395  // Get versions numbers
396  array<string> fromNums = {};
397  vFrom.Split(VERSION_DOT, fromNums, false);
398 
399  array<string> toNums = {};
400  vTo.Split(VERSION_DOT, toNums, false);
401 
402  // Setup count by smaller array
403  int count = fromNums.Count();
404  if (count > toNums.Count())
405  count = toNums.Count();
406 
407  for (int i; i < count; i++)
408  {
409  int iFrom = fromNums[i].ToInt();
410  int iTo = toNums[i].ToInt();
411 
412  // Compare version
413  if (iFrom < iTo)
414  {
415  // Current is old
416  return SCR_ComparerOperator.LESS_THAN;
417  }
418  else if (iFrom > iTo)
419  {
420  // Current is newer
421  return SCR_ComparerOperator.GREATER_THAN;
422  }
423  else if (iFrom == iTo)
424  {
425  // Versions are same
426  int end = vTo.IndexOfFrom(i, VERSION_DOT) - 1;
427 
428  if (end == -1)
429  return SCR_ComparerOperator.EQUAL;
430  }
431  }
432 
433  return -1;
434  }
435 
436  //------------------------------------------------------------------------------------------------
439  static SCR_ERevisionAvailability ItemAvailability(notnull WorkshopItem item)
440  {
441  Revision currentRev = item.GetActiveRevision();
442  if (!currentRev)
443  {
444  // Downloading was not finished
445  if (item.GetDownloadingRevision())
446  return SCR_ERevisionAvailability.ERA_DOWNLOAD_NOT_FINISHED;
447 
448  // No current version but item has offline flag
449  if (item.GetStateFlags() & EWorkshopItemState.EWSTATE_OFFLINE)
450  Print("ItemAvailability() - Can't find item offline although offline flag is present " + item.Name() + " current revision", LogLevel.WARNING);
451 
452  return SCR_ERevisionAvailability.ERA_UNKNOWN_AVAILABILITY;
453  }
454 
455  ERevisionAvailability currentAvailability = currentRev.GetAvailability();
456 
457  // Addon is ok
458  if (currentAvailability == ERevisionAvailability.ERA_AVAILABLE)
459  return SCR_ERevisionAvailability.ERA_AVAILABLE;
460 
461  Revision latestRev = item.GetLatestRevision();
462  ERevisionAvailability latestAvailability = latestRev.GetAvailability();
463 
464  if (!latestRev)
465  {
466  Print("ItemAvailability() - Can't compare availability of item " + item.Name() + " latest revision", LogLevel.WARNING);
467  return SCR_ERevisionAvailability.ERA_DELETED;
468  }
469 
470  // Deleted
471  if (currentAvailability == ERevisionAvailability.ERA_DELETED)
472  {
473  // Update is available
474  if (latestAvailability == ERevisionAvailability.ERA_AVAILABLE)
475  return SCR_ERevisionAvailability.ERA_COMPATIBLE_UPDATE_AVAILABLE;
476 
477  return SCR_ERevisionAvailability.ERA_DELETED;
478  }
479 
480  // Current addon is obsolete (not compatible with client version)
481  if (currentAvailability == ERevisionAvailability.ERA_OBSOLETE)
482  {
483  // Update is available
484  if (latestAvailability == ERevisionAvailability.ERA_AVAILABLE)
485  return SCR_ERevisionAvailability.ERA_COMPATIBLE_UPDATE_AVAILABLE;
486 
487  // No compatible update available
488  return SCR_ERevisionAvailability.ERA_OBSOLETE;
489  }
490 
491  return SCR_ERevisionAvailability.ERA_AVAILABLE;
492  }
493 
494  protected SCR_LoadingOverlay m_LoadingOverlay;
495 
496  protected ref ScriptInvoker<SCR_WorkshopItem, int> Event_OnAddonEnabled;
497  protected ref ScriptInvoker<> Event_OnAllAddonsEnabled;
498 
499  //------------------------------------------------------------------------------------------------
500  protected void InvokeEventOnAddonEnabled(SCR_WorkshopItem arg0, int arg1)
501  {
502  if (Event_OnAddonEnabled)
503  Event_OnAddonEnabled.Invoke(arg0, arg1);
504  }
505 
506  //------------------------------------------------------------------------------------------------
507  ScriptInvoker GetEventOnAddonEnabled()
508  {
509  if (!Event_OnAddonEnabled)
510  Event_OnAddonEnabled = new ScriptInvoker();
511 
512  return Event_OnAddonEnabled;
513  }
514 
515  //------------------------------------------------------------------------------------------------
516  protected void InvokeEventOnAllAddonsEnabled()
517  {
518  if (Event_OnAllAddonsEnabled)
519  Event_OnAllAddonsEnabled.Invoke();
520  }
521 
522  //------------------------------------------------------------------------------------------------
523  ScriptInvoker GetEventOnAllAddonsEnabled()
524  {
525  if (!Event_OnAllAddonsEnabled)
526  Event_OnAllAddonsEnabled = new ScriptInvoker();
527 
528  return Event_OnAllAddonsEnabled;
529  }
530 
531  //-----------------------------------------------------------------------------------------------
533  void EnableMultipleAddons(array<ref SCR_WorkshopItem> items, bool enable)
534  {
535  int count = items.Count() - 1;
536 
537  GetGame().GetCallqueue().CallLater(EnableAddonsRecursively, 0, false, items, count, enable);
538 
539  //m_LoadingOverlay = SCR_LoadingOverlayDialog.Create();
540  if (!m_LoadingOverlay)
541  m_LoadingOverlay = SCR_LoadingOverlay.ShowForWidget(GetGame().GetWorkspace(), string.Empty);
542  else
543  m_LoadingOverlay.SetShown(true);
544  }
545 
546  //-----------------------------------------------------------------------------------------------
547  protected void EnableAddonsRecursively(array<ref SCR_WorkshopItem> addons, out int remaining, bool enable)
548  {
549  if (remaining > -1)
550  {
551  addons[remaining].SetEnabled(enable);
552  InvokeEventOnAddonEnabled(addons[remaining], remaining);
553  remaining--;
554 
555  GetGame().GetCallqueue().CallLater(EnableAddonsRecursively, 0, false, addons, remaining, enable);
556  }
557  else
558  {
559  if (m_LoadingOverlay)
560  {
561  m_LoadingOverlay.SetShown(false);
562  InvokeEventOnAllAddonsEnabled();
563  }
564  else
565  {
566  InvokeEventOnAllAddonsEnabled();
567  }
568  }
569  }
570 
571  //-----------------------------------------------------------------------------------------------
572  //-----------------------------------------------------------------------------------------------
573  //-----------------------------------------------------------------------------------------------
574 
575 
576 
577 
578 
579 
580 
581 
582  //-----------------------------------------------------------------------------------------------
583  // P R O T E C T E D / P R I V A T E
584  //-----------------------------------------------------------------------------------------------
585 
586  //-----------------------------------------------------------------------------------------------
588  protected void Internal_CheckAddons()
589  {
590  _print("Internal_CheckAddons()", LogLevel.NORMAL);
591 
592 
593  if (!m_AddonCheckCallback)
594  m_AddonCheckCallback = new SCR_BackendCallback();
595 
596  WorkshopApi api = GetGame().GetBackendApi().GetWorkshop();
597 
598  if (api)
599  {
600  m_AddonCheckCallback.GetEventOnResponse().Insert(AddonCheckResponse);
601  api.OnItemsChecked(m_AddonCheckCallback);
602  }
603  }
604 
605  //-----------------------------------------------------------------------------------------------
606  protected void RegisterNewItem(string id, SCR_WorkshopItem itemWrapper)
607  {
608  #ifdef WORKSHOP_DEBUG
609  _print(string.Format("Registered new item: %1", itemWrapper.GetName()));
610  #endif
611 
612  itemWrapper.m_OnOfflineStateChanged.Insert(Callback_OnAddonOfflineStateChanged);
613  itemWrapper.m_OnReportStateChanged.Insert(Callback_OnAddonReportStateChanged);
614  itemWrapper.m_OnDownloadComplete.Insert(CountOutdatedAddons);
615 
616  m_mItems.Insert(id, itemWrapper);
617 
618  CountOutdatedAddons();
619  }
620 
621  //-----------------------------------------------------------------------------------------------
622  protected string GetItemId(WorkshopItem item)
623  {
624  return item.Id();
625  }
626 
627  //-----------------------------------------------------------------------------------------------
628  protected string GetItemId(Dependency item)
629  {
630  return item.GetID();
631  }
632 
633  //-----------------------------------------------------------------------------------------------
634  protected static bool CheckQueryFlag(SCR_WorkshopItem item, EWorkshopItemQuery flag)
635  {
636  switch(flag)
637  {
638  case EWorkshopItemQuery.ONLINE: return item.GetOnline();
639  case EWorkshopItemQuery.NOT_ONLINE: return !item.GetOnline();
640  case EWorkshopItemQuery.OFFLINE: return item.GetOffline();
641  case EWorkshopItemQuery.NOT_OFFLINE: return !item.GetOffline();
642  case EWorkshopItemQuery.ENABLED: return item.GetEnabled();
643  case EWorkshopItemQuery.NOT_ENABLED: return !item.GetEnabled();
644  case EWorkshopItemQuery.BLOCKED: return item.GetBlocked();
645  case EWorkshopItemQuery.RESTRICTED: return item.GetRestricted();
646  case EWorkshopItemQuery.REPORTED_BY_ME: return item.GetReportedByMe();
647  case EWorkshopItemQuery.LOCAL_VERSION_MATCH_DEPENDENCY: return item.GetCurrentLocalVersionMatchDependency();
648  case EWorkshopItemQuery.NOT_LOCAL_VERSION_MATCH_DEPENDENCY: return !item.GetCurrentLocalVersionMatchDependency();
649  case EWorkshopItemQuery.UPDATE_AVAILABLE: return item.GetUpdateAvailable();
650  case EWorkshopItemQuery.DEPENDENCY_UPDATE_AVAILABLE: return item.GetAnyDependencyUpdateAvailable();
651  case EWorkshopItemQuery.NO_PROBLEMS: return item.GetHighestPriorityProblem() == EWorkshopItemProblem.NO_PROBLEM;
652  case EWorkshopItemQuery.DEPENDENCY_MISSING: return item.GetAnyDependencyMissing();
653  case EWorkshopItemQuery.DEPENDENCY_NOT_MISSING: return !item.GetAnyDependencyMissing();
654  case EWorkshopItemQuery.ENABLED_AND_DEPENDENCY_DISABLED: return item.GetEnabledAndAnyDependencyDisabled();
655  case EWorkshopItemQuery.FAVOURITE: return item.GetFavourite();
656  case EWorkshopItemQuery.AUTHOR_BLOCKED: return item.GetModAuthorReportedByMe();
657  default: return false;
658  }
659  return false;
660  }
661 
662  //-----------------------------------------------------------------------------------------------
664  protected static bool CheckQueryFlagsAnd(SCR_WorkshopItem item, EWorkshopItemQuery flags)
665  {
666  // Could optimize to `if ((~item.m_Flags & flags) == 0)`, if we had these flags easily available.
667 
668  int currentFlag = 1;
669  for (int bit = 0; bit < 32; bit++)
670  {
671  if (flags & currentFlag) // If this flag is set in query
672  {
673  if (!CheckQueryFlag(item, currentFlag)) // False if any is false
674  return false;
675  }
676  currentFlag = currentFlag << 1;
677  }
678  return true; // True when all set flags are true
679  }
680 
681  //-----------------------------------------------------------------------------------------------
683  protected static bool CheckQueryFlagsOr(SCR_WorkshopItem item, EWorkshopItemQuery flags)
684  {
685  int currentFlag = 1;
686  for (int bit = 0; bit < 32; bit++)
687  {
688  if (flags & currentFlag) // If this flag is set in query
689  {
690  if (CheckQueryFlag(item, currentFlag)) // False if any is false
691  return true;
692  }
693  currentFlag = currentFlag << 1;
694  }
695  return false; // False when no conditions are true
696  }
697 
698 
699  //-----------------------------------------------------------------------------------------------
700  private void SCR_AddonManager(IEntitySource src, IEntity parent)
701  {
702  SetEventMask(EntityEvent.FRAME | EntityEvent.INIT);
703  SetFlags(EntityFlags.NO_TREE | EntityFlags.NO_LINK);
704 
705  s_Instance = this;
706  }
707 
708  //-----------------------------------------------------------------------------------------------
709  private void ~SCR_AddonManager()
710  {
711  s_Instance = null;
712  }
713 
714 
715  //-----------------------------------------------------------------------------------------------
717  protected void FinalizeInitAfterAsyncChecks()
718  {
719  if (m_bInitFinished || !m_bAddonsChecked )
720  return;
721 
722  _print("FinalizeInitAfterAsyncChecks()", LogLevel.NORMAL);
723 
724  m_bInitFinished = true;
725 
726  _print("Init Finished", LogLevel.NORMAL);
727  _print(string.Format(" User Workshop access: %1", GetReady()), LogLevel.NORMAL);
728  }
729 
730  //-----------------------------------------------------------------------------------------------
731  override void EOnInit(IEntity owner)
732  {
733  // Don't perform scan if not in actual game
734  if (SCR_Global.IsEditMode(this))
735  return;
736 
737  // Scan all offline items and register them
738  // Scan offline items if needed
739  WorkshopApi api = GetGame().GetBackendApi().GetWorkshop();
740  if (api)
741  {
742  api.ScanOfflineItems();
743 
744  // Get all offline addons and register them
745  array<WorkshopItem> items = {};
746  api.GetOfflineItems(items);
747 
748  // Prepare items to registration
749  foreach (WorkshopItem item : items)
750  {
751  m_aAddonsToRegister.Insert(item);
752  }
753  }
754 
755  // If user's UGC privilege is disabled, disable all addons
756  // If UGC is blocked, disable addons which are already downloaded
757  if (!GetUgcPrivilege())
758  {
759  foreach (SCR_WorkshopItem item : GetOfflineAddons())
760  item.SetEnabled(false);
761  }
762 
763  Internal_CheckAddons();
764 
765  if(GetGame().InPlayMode())
767  }
768 
769  //------------------------------------------------------------------------------------------------
770  protected void AddonCheckResponse(SCR_BackendCallback callback)
771  {
772  EBackendCallbackResponse type = callback.GetResponseType();
773 
774  if (type != EBackendCallbackResponse.SUCCESS)
775  {
776  // TODO: Notify error
777  return;
778  }
779 
780  // Success
781  Callback_CheckAddons_OnSuccess();
782  }
783 
784  //-----------------------------------------------------------------------------------------------
785  protected void Callback_CheckAddons_OnSuccess()
786  {
787  m_bAddonsChecked = true;
788 
789  _print("Callback_CheckAddons_OnSuccess()", LogLevel.NORMAL);
790 
791  array<ref SCR_WorkshopItem> pendingDownloads = {};
792 
793  // Update state of items after the check is complete
794  // This will make the addons instantly update list of dependencies and revisions
795  for (int i = 0; i < m_aAddonsToRegister.Count(); i++)
796  {
797  SCR_WorkshopItem scrItem = Register(m_aAddonsToRegister[i]);
798  scrItem.Internal_OnAddonsChecked();
799  array<ref SCR_WorkshopItem> dependencies = scrItem.GetLoadedDependencies();
800 
801  if (scrItem.GetOffline())
802  pendingDownloads.Insert(scrItem);
803  }
804 
805  m_aAddonsToRegister.Clear();
806 
807  FinalizeInitAfterAsyncChecks();
808  //SCR_DownloadManager.GetInstance().DownloadAddons(pendingDownloads);
809 
810  m_OnAddonsChecked.Invoke();
811 
812  //show reload failure
813  array<string> addonIds = {};
814  GetGame().ReloadFailureAddons(addonIds);
815  array<ref SCR_WorkshopItem> arr = {};
816  foreach(auto addon: addonIds)
817  {
818  SCR_WorkshopItem wi = GetItem(addon);
819  if (wi)
820  arr.Insert(wi);
821  }
822 
823  if (!arr.IsEmpty())
824  SCR_AddonListDialog.CreateFailedToStartWithMods(arr);
825  }
826 
827  //-----------------------------------------------------------------------------------------------
828  protected void Callback_GetPrivilege_OnPrivilegeResult(UserPrivilege privilege, UserPrivilegeResult result)
829  {
830  _print("Callback_GetPrivilege_OnPrivilegeResult()", LogLevel.NORMAL);
831 
832  if (privilege == UserPrivilege.USER_GEN_CONTENT)
833  {
834  bool allowed = result == UserPrivilegeResult.ALLOWED;
835 
836  _print(string.Format(" UserPrivilege.USER_GEN_CONTENT: %1", allowed), LogLevel.NORMAL);
837 
838  m_OnUgcPrivilegeResult.Invoke(allowed);
839  }
840  }
841 
842 
843  //-----------------------------------------------------------------------------------------------
845  protected void Callback_OnAddonOfflineStateChanged(SCR_WorkshopItem item, bool newState)
846  {
847  m_OnAddonOfflineStateChanged.Invoke(item, newState);
848  }
849 
850  //-----------------------------------------------------------------------------------------------
852  protected void Callback_OnAddonReportStateChanged(SCR_WorkshopItem item, bool newReport)
853  {
854  m_OnAddonReportedStateChanged.Invoke(item, newReport);
855  }
856 
857 
858  //-----------------------------------------------------------------------------------------------
859  override void EOnFrame(IEntity owner, float timeSlice)
860  {
861  array<string> unregisterIds;
862  array<SCR_WorkshopItem> updateItems;
863 
864  // Iterate all items
865  // Existins utems are updated
866  // Non-existant items are marked for deletion from the map
867  foreach (string id, SCR_WorkshopItem item : m_mItems)
868  {
869  // Item can be unregistered when no actions are running
870  // And when the addon manager holds the only reference to it
871  int refCount = item.GetRefCount();
872  bool canBeUnregistered = item.Internal_GetCanBeUnregistered();
873  if (canBeUnregistered && item.GetRefCount() == 2) // SCR_WorkshopItem item above also holds one reference! The other reference is the map entry.
874  {
875  if (!unregisterIds)
876  unregisterIds = {};
877 
878  unregisterIds.Insert(id);
879  }
880  else
881  {
882  if (!updateItems)
883  updateItems = {};
884 
885  // Don't update items directly in case an update causes an insertion of a new item
886  updateItems.Insert(item);
887  }
888  }
889 
890  // Update the marked entries
891  if (updateItems)
892  {
893  foreach (SCR_WorkshopItem item : updateItems)
894  item.Internal_Update(timeSlice);
895  }
896 
897  // Delete the marked entries
898  if (unregisterIds)
899  {
900  #ifdef WORKSHOP_DEBUG
901  _print(string.Format("Unregistering items: %1", unregisterIds.Count()));
902  #endif
903 
904  foreach (string id : unregisterIds)
905  {
906  #ifdef WORKSHOP_DEBUG
907  SCR_WorkshopItem item = m_mItems.Get(id);
908  _print(string.Format("Unregistered item: %1", item.GetName()));
909  #endif
910 
911  m_mItems.Remove(id);
912  }
913  }
914 
915  // Detection of case when addons get enabled/disabled
916  // Iterate all addons and invoke event whenever current list of enabled addons is different from prev. list
917  // It's not a perfect implementation but it's fine in main menu
918  m_fAddonsEnabledTimer += timeSlice;
919  if (m_fAddonsEnabledTimer > ADDONS_ENABLED_UPDATE_INTERVAL_S)
920  {
921  string addonsEnabled;
922  foreach (string id, SCR_WorkshopItem item : m_mItems)
923  {
924  if (item.GetEnabled())
925  addonsEnabled = addonsEnabled + id + " ";
926  }
927 
928  if (addonsEnabled != m_sAddonsEnabledPrev)
929  {
930  m_OnAddonsEnabledChanged.Invoke();
931  m_sAddonsEnabledPrev = addonsEnabled;
932  }
933 
934  m_fAddonsEnabledTimer = 0;
935  }
936 
937  // Count and cache amount of outdated addons
938  // This doesn't need to happen often, 1s is enough
939 
940  /*
941  m_fAddonsOutdatedTimer += timeSlice;
942  if (m_fAddonsOutdatedTimer > ADDONS_OUTDATED_UPDATE_INTERVAL_S)
943  {
944  m_iAddonsOutdated = CountItemsBasic(GetOfflineAddons(), EWorkshopItemQuery.UPDATE_AVAILABLE);
945 
946  m_fAddonsOutdatedTimer = 0;
947  }
948  */
949  }
950 
951 
952  //-----------------------------------------------------------------------------------------------
954  void Internal_OnNewDownload(SCR_WorkshopItem item, SCR_WorkshopItemActionDownload action)
955  {
956  m_OnNewDownload.Invoke(item, action);
957  }
958 
959  //-----------------------------------------------------------------------------------------------
961  protected void CountOutdatedAddons()
962  {
963  m_iAddonsOutdated = 0;
964  array<ref SCR_WorkshopItem> items = GetOfflineAddons();
965 
966  foreach (SCR_WorkshopItem item : items)
967  {
968  auto wi = item.Internal_GetWorkshopItem();
969  if (!wi || !wi.HasLatestVersion())
970  m_iAddonsOutdated++;
971  }
972  }
973 
974  //-----------------------------------------------------------------------------------------------
975  // Handling of addon presets
976 
977  //----------------------------------------------------------------------------------------------------------------------------------------------------
978  SCR_WorkshopAddonManagerPresetStorage GetPresetStorage()
979  {
980  return m_Storage;
981  }
982 
983 
984  //----------------------------------------------------------------------------------------------------------------------------------------------------
985  // Looks up addon from internal map
986  SCR_WorkshopItem GetItem(string id)
987  {
988  return m_mItems.Get(id);
989  }
990 
991  protected SCR_WorkshopAddonPreset m_SelectedPreset;
992  protected ref array<ref SCR_WorkshopAddonPresetAddonMeta> m_aAddonsNotFound = {};
993 
994  //----------------------------------------------------------------------------------------------------------------------------------------------------
995  // Enables addons from preset, disables all addons from other presets
996  void SelectPreset(SCR_WorkshopAddonPreset preset, notnull array<ref SCR_WorkshopAddonPresetAddonMeta> addonsNotFound)
997  {
998  array<ref SCR_WorkshopItem> addons = GetOfflineAddons();
999  GetEventOnAllAddonsEnabled().Insert(OnOfflineAddonsDisabled);
1000  m_SelectedPreset = preset;
1001  EnableMultipleAddons(addons, false);
1002  }
1003 
1004  //-----------------------------------------------------------------------------------------------
1005  protected void OnOfflineAddonsDisabled()
1006  {
1007  GetEventOnAllAddonsEnabled().Remove(OnOfflineAddonsDisabled);
1008 
1009  array<ref SCR_WorkshopAddonPresetAddonMeta> enabledAddons = m_SelectedPreset.GetAddons();
1010  array<ref SCR_WorkshopItem> addons = {};
1011 
1012  foreach (SCR_WorkshopAddonPresetAddonMeta meta : enabledAddons)
1013  {
1014  string guid = meta.GetGuid();
1015  SCR_WorkshopItem item = GetItem(guid);
1016 
1017  if (item && item.GetOffline())
1018  addons.Insert(item);
1019  else
1020  m_aAddonsNotFound.Insert(meta);
1021  }
1022 
1023  GetEventOnAllAddonsEnabled().Insert(OnAllAddonsEnabledCorrupted);
1024  EnableMultipleAddons(addons, true);
1025  m_SelectedPreset = null;
1026  }
1027 
1028  //-----------------------------------------------------------------------------------------------
1030  protected void OnAllAddonsEnabledCorrupted()
1031  {
1032  if (!m_aAddonsNotFound.IsEmpty())
1033  {
1034  new SCR_WorkshopErrorPresetLoadDialog(m_aAddonsNotFound);
1035  m_aAddonsNotFound.Clear();
1036  }
1037 
1038  GetEventOnAllAddonsEnabled().Remove(OnAllAddonsEnabledCorrupted);
1039  }
1040 
1041 
1042  //----------------------------------------------------------------------------------------------------------------------------------------------------
1043  SCR_WorkshopAddonPreset CreatePresetFromEnabledAddons(string presetName)
1044  {
1045  // Get GUIDs of addons which are enabled
1046 
1047  array<ref SCR_WorkshopItem> enabledAddons = SCR_AddonManager.SelectItemsBasic(GetOfflineAddons(), EWorkshopItemQuery.ENABLED);
1048 
1049  array<ref SCR_WorkshopAddonPresetAddonMeta> addonsMeta = {};
1050  foreach (SCR_WorkshopItem item : enabledAddons)
1051  {
1052  string guid = item.GetId();
1053  addonsMeta.Insert((new SCR_WorkshopAddonPresetAddonMeta()).Init(guid, item.GetName()) );
1054  }
1055 
1056  if (addonsMeta.IsEmpty())
1057  return null;
1058 
1059  SCR_WorkshopAddonPreset preset = (new SCR_WorkshopAddonPreset()).Init(presetName, addonsMeta);
1060 
1061  return preset;
1062  }
1063 
1064  //----------------------------------------------------------------------------------------------------------------------------------------------------
1066  int CountOfEnabledAddons()
1067  {
1068  array<ref SCR_WorkshopItem> enabledAddons = SCR_AddonManager.SelectItemsBasic(GetOfflineAddons(), EWorkshopItemQuery.ENABLED);
1069  return enabledAddons.Count();
1070  }
1071 
1072  //-----------------------------------------------------------------------------------------------
1073  map<string, ref SCR_WorkshopItem> GetItemsMap()
1074  {
1075  return m_mItems;
1076  }
1077 
1078  //-----------------------------------------------------------------------------------------------
1079  //-----------------------------------------------------------------------------------------------
1080  //-----------------------------------------------------------------------------------------------
1081 
1082 
1083  //------------------------------------------------------------------------------------------------
1084  void _print(string str, LogLevel logLevel = LogLevel.DEBUG)
1085  {
1086  Print(string.Format("[SCR_AddonManager] %1 %2", this, str), logLevel);
1087  }
1088 };
1089 
1090 enum SCR_ERevisionAvailability : ERevisionAvailability
1091 {
1095 }
EWorkshopItemProblem
EWorkshopItemProblem
Definition: SCR_WorkshopItem.c:11
SCR_ERevisionAvailability
SCR_ERevisionAvailability
Definition: SCR_AddonManager.c:1090
DEPENDENCY_UPDATE_AVAILABLE
@ DEPENDENCY_UPDATE_AVAILABLE
Definition: SCR_AddonManager.c:54
RESTRICTED
@ RESTRICTED
Definition: SCR_AddonManager.c:47
EntityEditorProps
enum EQueryType EntityEditorProps(category:"GameScripted/Sound", description:"THIS IS THE SCRIPT DESCRIPTION.", color:"0 0 255 255")
Definition: SCR_AmbientSoundsComponent.c:12
NOT_ONLINE
@ NOT_ONLINE
Definition: SCR_AddonManager.c:37
FAVOURITE
@ FAVOURITE
Definition: SCR_AddonManager.c:62
GetGame
ArmaReforgerScripted GetGame()
Definition: game.c:1424
SCR_ScriptPlatformRequestCallback
Definition: SCR_OnlineServiceWorkshop.c:2
SCR_WorkshopAddonPresetAddonMeta
Meta data of one addon.
Definition: SCR_WorkshopAddonManagerStorage.c:3
ERA_UNKNOWN_AVAILABILITY
@ ERA_UNKNOWN_AVAILABILITY
Definition: SCR_AddonManager.c:1094
SCR_WorkshopItemActionDownload
Definition: SCR_WorkshopItemActionDownload.c:9
REPORTED_BY_ME
@ REPORTED_BY_ME
Definition: SCR_AddonManager.c:46
GenericEntity
SCR_GenericBoxEntityClass GenericEntity
Init
void Init(IEntity entity=null, vector worldPos=vector.Zero, float timestamp=0.0, EAITargetInfoCategory category=0)
Definition: SCR_AITargetInfo.c:27
SCR_WorkshopItem
Definition: SCR_WorkshopItem.c:21
DEPENDENCY_MISSING
@ DEPENDENCY_MISSING
Definition: SCR_AddonManager.c:58
SCR_LoadingOverlay
Definition: SCR_LoadingOverlay.c:49
OFFLINE
@ OFFLINE
Definition: SCR_AddonManager.c:39
SCR_BackendCallback
Scripted backend callback class unifying backend response.
Definition: SCR_BackendCallback.c:21
ERA_COMPATIBLE_UPDATE_AVAILABLE
@ ERA_COMPATIBLE_UPDATE_AVAILABLE
Definition: SCR_AddonManager.c:1092
AUTHOR_BLOCKED
@ AUTHOR_BLOCKED
Definition: SCR_AddonManager.c:64
SCR_ComparerOperator
SCR_ComparerOperator
Definition: SCR_Comparer.c:2
s_Instance
SCR_SpawnerSlotManagerClass s_Instance
Class used for managing changes and removals of slots present in world.
m_LoadingOverlay
protected SCR_LoadingOverlay m_LoadingOverlay
Definition: SCR_BackendImageComponent.c:250
NO_PROBLEMS
@ NO_PROBLEMS
Definition: SCR_AddonManager.c:56
SCR_AddonManagerClass
Definition: SCR_AddonManager.c:68
NOT_LOCAL_VERSION_MATCH_DEPENDENCY
@ NOT_LOCAL_VERSION_MATCH_DEPENDENCY
Definition: SCR_AddonManager.c:50
SCR_WorkshopCallbackBase
Definition: SCR_OnlineServiceWorkshop.c:101
NOT_OFFLINE
@ NOT_OFFLINE
Definition: SCR_AddonManager.c:40
SCR_Global
Definition: Functions.c:6
LOCAL_VERSION_MATCH_DEPENDENCY
@ LOCAL_VERSION_MATCH_DEPENDENCY
Definition: SCR_AddonManager.c:49
type
EDamageType type
Definition: SCR_DestructibleTreeV2.c:32
SCR_AddonManager
Definition: SCR_AddonManager.c:72
EBackendCallbackResponse
EBackendCallbackResponse
Basic callback responses.
Definition: SCR_BackendCallback.c:12
NOT_ENABLED
@ NOT_ENABLED
Definition: SCR_AddonManager.c:43
ERA_DOWNLOAD_NOT_FINISHED
@ ERA_DOWNLOAD_NOT_FINISHED
Definition: SCR_AddonManager.c:1093
ENABLED
@ ENABLED
Definition: SCR_AddonManager.c:42
callback
DownloadConfigCallback callback
SCR_AddonListDialog
Shows a list of addons and some text.
Definition: SCR_WorkshopUiCommon.c:820
SCR_WorkshopErrorPresetLoadDialog
Definition: AddonManagerDialogs.c:65
m_Storage
BaseInventoryStorageComponent m_Storage
Definition: SCR_TourniquetStorageComponent.c:10
EWorkshopItemQuery
EWorkshopItemQuery
Definition: SCR_AddonManager.c:34
BLOCKED
@ BLOCKED
Definition: SCR_AddonManager.c:45
DEPENDENCY_NOT_MISSING
@ DEPENDENCY_NOT_MISSING
Definition: SCR_AddonManager.c:59
ONLINE
@ ONLINE
Definition: SCR_AddonManager.c:36
UPDATE_AVAILABLE
@ UPDATE_AVAILABLE
Definition: SCR_AddonManager.c:52
ENABLED_AND_DEPENDENCY_DISABLED
@ ENABLED_AND_DEPENDENCY_DISABLED
Definition: SCR_AddonManager.c:60
category
params category
Definition: SCR_VehicleDamageManagerComponent.c:180
SCR_WorkshopAddonManagerPresetStorage
Definition: SCR_WorkshopAddonManagerStorage.c:110