From c2f4cb3ec55f00f187c13fe60a2f95fdfe65b353 Mon Sep 17 00:00:00 2001 From: Vitaly Popuzin <35366872+popuz@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:59:07 +0200 Subject: [PATCH] Feat: used emotes analytics (#1598) ## What does this PR change? - add ECS System for triggered emotes (by player or by scene) - add Decorator to handle equipped emote ## How to test the changes? 1. Launch the explorer 2. Verify "happy path" is not broken (and emotes are playing as previously - by player and by scene) --- .../Emotes/Components/CharacterEmoteIntent.cs | 9 + .../DCL/AvatarRendering/Emotes/Emote.cs | 10 +- .../Emotes/Systems/RemoteEmotesSystem.cs | 1 + .../Emotes/Systems/UpdateEmoteInputSystem.cs | 2 +- .../AvatarSection/AvatarController.cs | 2 +- .../Backpack/BackpackBus/IBackpackEventBus.cs | 6 +- .../Assets/DCL/Backpack/BackpackController.cs | 4 +- .../DCL/Backpack/BackpackGridController.cs | 4 +- .../BackpackInfoPanelController.cs | 4 +- .../DCL/Backpack/BackpackSlotsController.cs | 4 +- .../BackpackCharacterPreviewController.cs | 4 +- .../BackpackEmoteGridController.cs | 4 +- .../BackpackEmoteSlotsController.cs | 4 +- .../CharacterPreviewController.cs | 2 +- .../Analytics/AnalyticsConfiguration.asset | 4 + .../Analytics/AnalyticsController.cs | 9 +- .../Analytics/AnalyticsEvents.cs | 5 + .../Analytics/DCL.Analytics.asmdef | 6 +- .../BackpackEventBusDecorator.cs | 160 ++++++++++++++++++ .../BackpackEventBusDecorator.cs.meta | 3 + .../Analytics/StaticCommonTraitsPlugin.cs | 3 + .../Systems/AnalyticsEmotesSystem.cs | 65 +++++++ .../Systems/AnalyticsEmotesSystem.cs.meta | 3 + .../PlayerParcelChangedAnalyticsSystem.cs | 2 + .../TimeSpentInWorldAnalyticsSystem.cs | 2 + .../PluginSystem/Global/AnalyticsPlugin.cs | 1 + .../PluginSystem/Global/BackpackSubPlugin.cs | 7 +- .../PluginSystem/Global/ExplorePanelPlugin.cs | 10 +- .../RestrictedActions/GlobalWorldActions.cs | 2 +- .../Global/Dynamic/DynamicWorldContainer.cs | 15 +- 30 files changed, 320 insertions(+), 37 deletions(-) create mode 100644 Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/DecoratorBased/BackpackEventBusDecorator.cs create mode 100644 Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/DecoratorBased/BackpackEventBusDecorator.cs.meta create mode 100644 Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/AnalyticsEmotesSystem.cs create mode 100644 Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/AnalyticsEmotesSystem.cs.meta diff --git a/Explorer/Assets/DCL/AvatarRendering/Emotes/Components/CharacterEmoteIntent.cs b/Explorer/Assets/DCL/AvatarRendering/Emotes/Components/CharacterEmoteIntent.cs index b7541b0442..c30a07b4be 100644 --- a/Explorer/Assets/DCL/AvatarRendering/Emotes/Components/CharacterEmoteIntent.cs +++ b/Explorer/Assets/DCL/AvatarRendering/Emotes/Components/CharacterEmoteIntent.cs @@ -2,9 +2,18 @@ namespace DCL.AvatarRendering.Emotes { + public enum TriggerSource + { + PREVIEW, + SELF, + REMOTE, + SCENE, + } + public struct CharacterEmoteIntent { public URN EmoteId; public bool Spatial; + public TriggerSource TriggerSource; } } diff --git a/Explorer/Assets/DCL/AvatarRendering/Emotes/Emote.cs b/Explorer/Assets/DCL/AvatarRendering/Emotes/Emote.cs index fdb3499053..4f644f20d9 100644 --- a/Explorer/Assets/DCL/AvatarRendering/Emotes/Emote.cs +++ b/Explorer/Assets/DCL/AvatarRendering/Emotes/Emote.cs @@ -17,11 +17,11 @@ public class Emote : IEmote public bool IsLoading { get; set; } = true; - public bool IsOnChain() - { - var id = ((IAvatarAttachment) this).GetUrn().ToString(); - return id.StartsWith("urn:") && !id.StartsWith("urn:decentraland:off-chain:"); - } + public bool IsOnChain() => + IsOnChain(id: ((IAvatarAttachment) this).GetUrn().ToString()); + + public static bool IsOnChain(string id) => + id.StartsWith("urn:") && !id.StartsWith("urn:decentraland:off-chain:"); public AvatarAttachmentDTO GetDTO() => Model.Asset!; diff --git a/Explorer/Assets/DCL/AvatarRendering/Emotes/Systems/RemoteEmotesSystem.cs b/Explorer/Assets/DCL/AvatarRendering/Emotes/Systems/RemoteEmotesSystem.cs index 790f8d7a42..6e73e73f6a 100644 --- a/Explorer/Assets/DCL/AvatarRendering/Emotes/Systems/RemoteEmotesSystem.cs +++ b/Explorer/Assets/DCL/AvatarRendering/Emotes/Systems/RemoteEmotesSystem.cs @@ -55,6 +55,7 @@ protected override void Update(float t) ref CharacterEmoteIntent intention = ref World.AddOrGet(entity); intention.EmoteId = remoteEmoteIntention.EmoteId; intention.Spatial = true; + intention.TriggerSource = TriggerSource.REMOTE; } } diff --git a/Explorer/Assets/DCL/AvatarRendering/Emotes/Systems/UpdateEmoteInputSystem.cs b/Explorer/Assets/DCL/AvatarRendering/Emotes/Systems/UpdateEmoteInputSystem.cs index 9bf2c04cf9..5091831fb7 100644 --- a/Explorer/Assets/DCL/AvatarRendering/Emotes/Systems/UpdateEmoteInputSystem.cs +++ b/Explorer/Assets/DCL/AvatarRendering/Emotes/Systems/UpdateEmoteInputSystem.cs @@ -97,7 +97,7 @@ private void TriggerEmote([Data] int emoteIndex, in Entity entity, in Profile pr if (emoteId.IsNullOrEmpty()) return; - var newEmoteIntent = new CharacterEmoteIntent { EmoteId = emoteId, Spatial = true }; + var newEmoteIntent = new CharacterEmoteIntent { EmoteId = emoteId, Spatial = true, TriggerSource = TriggerSource.SELF}; ref var emoteIntent = ref World.AddOrGet(entity, newEmoteIntent); emoteIntent = newEmoteIntent; diff --git a/Explorer/Assets/DCL/Backpack/AvatarSection/AvatarController.cs b/Explorer/Assets/DCL/Backpack/AvatarSection/AvatarController.cs index d434cac205..0a4ab3489a 100644 --- a/Explorer/Assets/DCL/Backpack/AvatarSection/AvatarController.cs +++ b/Explorer/Assets/DCL/Backpack/AvatarSection/AvatarController.cs @@ -19,7 +19,7 @@ public AvatarController(AvatarView view, AvatarSlotView[] slotViews, NftTypeIconSO rarityBackgrounds, BackpackCommandBus backpackCommandBus, - BackpackEventBus backpackEventBus, + IBackpackEventBus backpackEventBus, BackpackGridController backpackGridController, BackpackInfoPanelController backpackInfoPanelController, IThumbnailProvider thumbnailProvider, diff --git a/Explorer/Assets/DCL/Backpack/BackpackBus/IBackpackEventBus.cs b/Explorer/Assets/DCL/Backpack/BackpackBus/IBackpackEventBus.cs index 2caef11921..d032816ec7 100644 --- a/Explorer/Assets/DCL/Backpack/BackpackBus/IBackpackEventBus.cs +++ b/Explorer/Assets/DCL/Backpack/BackpackBus/IBackpackEventBus.cs @@ -21,9 +21,11 @@ public interface IBackpackEventBus public event Action FilterCategoryEvent; public event Action SearchEvent; public event Action ChangedBackpackSectionEvent; - public event Action? ChangeColorEvent; + public event Action? ChangeColorEvent; public event Action UnEquipAllEvent; event Action PublishProfileEvent; + event Action? FilterCategoryByEnumEvent; + event Action? DeactivateEvent; public void SendWearableSelect(IWearable equipWearable); @@ -52,5 +54,7 @@ public interface IBackpackEventBus void SendEmoteSlotSelect(int slot); public void SendChangedBackpackSectionEvent(BackpackSections backpackSections); + + public void SendBackpackDeactivateEvent(); } } diff --git a/Explorer/Assets/DCL/Backpack/BackpackController.cs b/Explorer/Assets/DCL/Backpack/BackpackController.cs index e182aaafa5..01e088c7b8 100644 --- a/Explorer/Assets/DCL/Backpack/BackpackController.cs +++ b/Explorer/Assets/DCL/Backpack/BackpackController.cs @@ -35,7 +35,7 @@ public class BackpackController : ISection, IDisposable private readonly Dictionary backpackSections; private readonly SectionSelectorController sectionSelectorController; private readonly Dictionary tabsBySections; - private readonly BackpackEventBus backpackEventBus; + private readonly IBackpackEventBus backpackEventBus; private BackpackSections lastShownSection; private CancellationTokenSource? animationCts; @@ -49,7 +49,7 @@ public BackpackController( AvatarView avatarView, NftTypeIconSO rarityInfoPanelBackgrounds, BackpackCommandBus backpackCommandBus, - BackpackEventBus backpackEventBus, + IBackpackEventBus backpackEventBus, BackpackGridController gridController, BackpackInfoPanelController wearableInfoPanelController, BackpackInfoPanelController emoteInfoPanelController, diff --git a/Explorer/Assets/DCL/Backpack/BackpackGridController.cs b/Explorer/Assets/DCL/Backpack/BackpackGridController.cs index 7867d2d9b6..e43328e7c9 100644 --- a/Explorer/Assets/DCL/Backpack/BackpackGridController.cs +++ b/Explorer/Assets/DCL/Backpack/BackpackGridController.cs @@ -42,7 +42,7 @@ public class BackpackGridController private readonly BackpackGridView view; private readonly BackpackCommandBus commandBus; - private readonly BackpackEventBus eventBus; + private readonly IBackpackEventBus eventBus; private readonly IWeb3IdentityCache web3IdentityCache; private readonly NftTypeIconSO rarityBackgrounds; private readonly NFTColorsSO rarityColors; @@ -70,7 +70,7 @@ public class BackpackGridController public BackpackGridController( BackpackGridView view, BackpackCommandBus commandBus, - BackpackEventBus eventBus, + IBackpackEventBus eventBus, IWeb3IdentityCache web3IdentityCache, NftTypeIconSO rarityBackgrounds, NFTColorsSO rarityColors, diff --git a/Explorer/Assets/DCL/Backpack/BackpackInfoPanel/BackpackInfoPanelController.cs b/Explorer/Assets/DCL/Backpack/BackpackInfoPanel/BackpackInfoPanelController.cs index 2b7c9f9362..f92d9ae802 100644 --- a/Explorer/Assets/DCL/Backpack/BackpackInfoPanel/BackpackInfoPanelController.cs +++ b/Explorer/Assets/DCL/Backpack/BackpackInfoPanel/BackpackInfoPanelController.cs @@ -17,7 +17,7 @@ public class BackpackInfoPanelController : IDisposable private const string EMOTE_CATEGORY = "emote"; private readonly BackpackInfoPanelView view; - private readonly BackpackEventBus backpackEventBus; + private readonly IBackpackEventBus backpackEventBus; private readonly NftTypeIconSO categoryIcons; private readonly NftTypeIconSO rarityInfoPanelBackgrounds; private readonly NFTColorsSO rarityColors; @@ -26,7 +26,7 @@ public class BackpackInfoPanelController : IDisposable public BackpackInfoPanelController( BackpackInfoPanelView view, - BackpackEventBus backpackEventBus, + IBackpackEventBus backpackEventBus, NftTypeIconSO categoryIcons, NftTypeIconSO rarityInfoPanelBackgrounds, NFTColorsSO rarityColors, diff --git a/Explorer/Assets/DCL/Backpack/BackpackSlotsController.cs b/Explorer/Assets/DCL/Backpack/BackpackSlotsController.cs index 194a7144f2..7cef600399 100644 --- a/Explorer/Assets/DCL/Backpack/BackpackSlotsController.cs +++ b/Explorer/Assets/DCL/Backpack/BackpackSlotsController.cs @@ -16,7 +16,7 @@ public class BackpackSlotsController : IDisposable private const int MAX_HIDES = 15; private readonly BackpackCommandBus backpackCommandBus; - private readonly BackpackEventBus backpackEventBus; + private readonly IBackpackEventBus backpackEventBus; private readonly NftTypeIconSO rarityBackgrounds; private readonly IThumbnailProvider thumbnailProvider; private readonly Dictionary avatarSlots = new (); @@ -29,7 +29,7 @@ public class BackpackSlotsController : IDisposable public BackpackSlotsController( AvatarSlotView[] avatarSlotViews, BackpackCommandBus backpackCommandBus, - BackpackEventBus backpackEventBus, + IBackpackEventBus backpackEventBus, NftTypeIconSO rarityBackgrounds, IThumbnailProvider thumbnailProvider) { diff --git a/Explorer/Assets/DCL/Backpack/CharacterPreview/BackpackCharacterPreviewController.cs b/Explorer/Assets/DCL/Backpack/CharacterPreview/BackpackCharacterPreviewController.cs index 6237afc899..0c67064aef 100644 --- a/Explorer/Assets/DCL/Backpack/CharacterPreview/BackpackCharacterPreviewController.cs +++ b/Explorer/Assets/DCL/Backpack/CharacterPreview/BackpackCharacterPreviewController.cs @@ -18,13 +18,13 @@ namespace DCL.Backpack.CharacterPreview { public class BackpackCharacterPreviewController : CharacterPreviewControllerBase { - private readonly BackpackEventBus backpackEventBus; + private readonly IBackpackEventBus backpackEventBus; private readonly IEquippedEmotes equippedEmotes; private CancellationTokenSource? emotePreviewCancellationToken; public BackpackCharacterPreviewController(CharacterPreviewView view, ICharacterPreviewFactory previewFactory, - BackpackEventBus backpackEventBus, + IBackpackEventBus backpackEventBus, World world, IEquippedEmotes equippedEmotes, CharacterPreviewEventBus characterPreviewEventBus) diff --git a/Explorer/Assets/DCL/Backpack/EmotesSection/BackpackEmoteGridController.cs b/Explorer/Assets/DCL/Backpack/EmotesSection/BackpackEmoteGridController.cs index 05b8612901..e799e48665 100644 --- a/Explorer/Assets/DCL/Backpack/EmotesSection/BackpackEmoteGridController.cs +++ b/Explorer/Assets/DCL/Backpack/EmotesSection/BackpackEmoteGridController.cs @@ -27,7 +27,7 @@ public class BackpackEmoteGridController : IDisposable private readonly BackpackGridView view; private readonly BackpackCommandBus commandBus; - private readonly BackpackEventBus eventBus; + private readonly IBackpackEventBus eventBus; private readonly IWeb3IdentityCache web3IdentityCache; private readonly NftTypeIconSO rarityBackgrounds; private readonly NFTColorsSO rarityColors; @@ -52,7 +52,7 @@ public class BackpackEmoteGridController : IDisposable public BackpackEmoteGridController( BackpackGridView view, BackpackCommandBus commandBus, - BackpackEventBus eventBus, + IBackpackEventBus eventBus, IWeb3IdentityCache web3IdentityCache, NftTypeIconSO rarityBackgrounds, NFTColorsSO rarityColors, diff --git a/Explorer/Assets/DCL/Backpack/EmotesSection/BackpackEmoteSlotsController.cs b/Explorer/Assets/DCL/Backpack/EmotesSection/BackpackEmoteSlotsController.cs index 8a23f5f081..8b8c51f6df 100644 --- a/Explorer/Assets/DCL/Backpack/EmotesSection/BackpackEmoteSlotsController.cs +++ b/Explorer/Assets/DCL/Backpack/EmotesSection/BackpackEmoteSlotsController.cs @@ -11,7 +11,7 @@ public class BackpackEmoteSlotsController : IDisposable { private const int MIN_WAIT_TIME = 500; - private readonly BackpackEventBus backpackEventBus; + private readonly IBackpackEventBus backpackEventBus; private readonly IBackpackCommandBus backpackCommandBus; private readonly NftTypeIconSO rarityBackgrounds; private readonly (EmoteSlotContainerView, CancellationTokenSource)[] avatarSlots; @@ -20,7 +20,7 @@ public class BackpackEmoteSlotsController : IDisposable public BackpackEmoteSlotsController( EmoteSlotContainerView[] avatarSlotViews, - BackpackEventBus backpackEventBus, + IBackpackEventBus backpackEventBus, IBackpackCommandBus backpackCommandBus, NftTypeIconSO rarityBackgrounds) { diff --git a/Explorer/Assets/DCL/Character/CharacterPreview/CharacterPreviewController.cs b/Explorer/Assets/DCL/Character/CharacterPreview/CharacterPreviewController.cs index 37623312d8..0550049d2a 100644 --- a/Explorer/Assets/DCL/Character/CharacterPreview/CharacterPreviewController.cs +++ b/Explorer/Assets/DCL/Character/CharacterPreview/CharacterPreviewController.cs @@ -104,7 +104,7 @@ private async UniTask WaitForAvatarInstantiatedAsync(CancellationToken ct) public void PlayEmote(string emoteId) { - globalWorld.Add(characterPreviewEntity, new CharacterEmoteIntent { EmoteId = emoteId }); + globalWorld.Add(characterPreviewEntity, new CharacterEmoteIntent { EmoteId = emoteId, TriggerSource = TriggerSource.PREVIEW}); } public void StopEmotes() diff --git a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/AnalyticsConfiguration.asset b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/AnalyticsConfiguration.asset index 94ce5c1ef4..6b700738ff 100644 --- a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/AnalyticsConfiguration.asset +++ b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/AnalyticsConfiguration.asset @@ -47,6 +47,10 @@ MonoBehaviour: isEnabled: 1 - eventName: passport_opened isEnabled: 1 + - groupName: Wearables + events: + - eventName: used_emote + isEnabled: 1 useLocalEnvVariableFallback: 0 flushSize: 20 flushInterval: 30 diff --git a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/AnalyticsController.cs b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/AnalyticsController.cs index 65c1b5655f..d48b6126b7 100644 --- a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/AnalyticsController.cs +++ b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/AnalyticsController.cs @@ -1,4 +1,5 @@ -using DCL.Web3.Identities; +#nullable enable +using DCL.Web3.Identities; using ECS; using JetBrains.Annotations; using Segment.Serialization; @@ -36,7 +37,7 @@ public AnalyticsController(IAnalyticsService analyticsService, AnalyticsConfigur }); } - public void SetCommonParam(IRealmData realmData, [CanBeNull] IWeb3IdentityCache identityCache, ExposedTransform playerTransform) + public void SetCommonParam(IRealmData realmData, IWeb3IdentityCache? identityCache, ExposedTransform playerTransform) { analytics.AddPlugin(new DynamicCommonTraitsPlugin(realmData, identityCache, playerTransform)); @@ -44,13 +45,13 @@ public void SetCommonParam(IRealmData realmData, [CanBeNull] IWeb3IdentityCache Identify(identityCache.Identity); } - public void Track(string eventName, JsonObject properties = null) + public void Track(string eventName, JsonObject? properties = null) { if (Configuration.EventIsEnabled(eventName)) analytics.Track(eventName, properties); } - public void Identify(IWeb3Identity identity) + public void Identify(IWeb3Identity? identity) { if (identity != null) analytics.Identify(identity.Address, new JsonObject diff --git a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/AnalyticsEvents.cs b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/AnalyticsEvents.cs index 1387811835..0273ac80ad 100644 --- a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/AnalyticsEvents.cs +++ b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/AnalyticsEvents.cs @@ -20,6 +20,11 @@ public static class World public const string TIME_SPENT_IN_WORLD = "time_spent_in_world"; } + public static class Wearables + { + public const string USED_EMOTE = "used_emote"; + } + public static class UI { public const string MESSAGE_SENT = "chat_message_sent"; diff --git a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/DCL.Analytics.asmdef b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/DCL.Analytics.asmdef index 604ce5f0f6..88e2f00cea 100644 --- a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/DCL.Analytics.asmdef +++ b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/DCL.Analytics.asmdef @@ -16,7 +16,11 @@ "GUID:9ca29a7d75ab84bab806a20bbd350fd8", "GUID:d0ec51c740809fd4680d3ea27279dca7", "GUID:007bff6000804d90ac597452fb69a4ee", - "GUID:ae249ee11a6e0ea4aa01cefc0246a151" + "GUID:ae249ee11a6e0ea4aa01cefc0246a151", + "GUID:5eabe9a3d4dd19d42a16208ea5411062", + "GUID:6830e2f56251b492e9934f1fafbc8c7d", + "GUID:45fc0f02fe4e57c4a93a421d8f6f53df", + "GUID:e169fa6683c924c7e99a85981a91d953" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/DecoratorBased/BackpackEventBusDecorator.cs b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/DecoratorBased/BackpackEventBusDecorator.cs new file mode 100644 index 0000000000..d128ccbf99 --- /dev/null +++ b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/DecoratorBased/BackpackEventBusDecorator.cs @@ -0,0 +1,160 @@ +using DCL.AvatarRendering.Emotes; +using DCL.AvatarRendering.Wearables.Components; +using DCL.Backpack.BackpackBus; +using DCL.CharacterPreview; +using DCL.UI; +using Segment.Serialization; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace DCL.PerformanceAndDiagnostics.Analytics +{ + public class BackpackEventBusAnalyticsDecorator : IBackpackEventBus + { + private readonly IBackpackEventBus core; + private readonly IAnalyticsController analytics; + + public event Action SelectWearableEvent; + public event Action EquipWearableEvent; + public event Action UnEquipWearableEvent; + public event Action EquipEmoteEvent; + public event Action UnEquipEmoteEvent; + public event Action EmoteSlotSelectEvent; + public event Action SelectEmoteEvent; + public event Action> ForceRenderEvent; + public event Action FilterCategoryEvent; + public event Action SearchEvent; + public event Action ChangedBackpackSectionEvent; + public event Action ChangeColorEvent; + public event Action UnEquipAllEvent; + public event Action PublishProfileEvent; + public event Action FilterCategoryByEnumEvent; + public event Action DeactivateEvent; + + public BackpackEventBusAnalyticsDecorator(IBackpackEventBus core, IAnalyticsController analytics) + { + this.core = core; + this.analytics = analytics; + + core.EquipEmoteEvent += ReEmitWithAnalytics; + + // Re-emit core events + core.SelectWearableEvent += OnSelectWearable; + core.EquipWearableEvent += OnEquipWearable; + core.UnEquipWearableEvent += OnUnEquipWearable; + core.UnEquipEmoteEvent += OnUnEquipEmote; + core.EmoteSlotSelectEvent += OnEmoteSlotSelect; + core.SelectEmoteEvent += OnSelectEmote; + core.ForceRenderEvent += OnForceRender; + core.FilterCategoryEvent += OnFilterCategory; + core.SearchEvent += OnSearch; + core.ChangedBackpackSectionEvent += OnChangedBackpackSection; + core.ChangeColorEvent += OnChangeColor; + core.UnEquipAllEvent += OnUnEquipAll; + core.PublishProfileEvent += OnPublishProfile; + core.FilterCategoryByEnumEvent += OnFilterCategoryByEnum; + core.DeactivateEvent += OnDeactivate; + } + + ~BackpackEventBusAnalyticsDecorator() + { + core.EquipEmoteEvent -= ReEmitWithAnalytics; + + // Unsubscribe from re-emited events + core.SelectWearableEvent -= OnSelectWearable; + core.EquipWearableEvent -= OnEquipWearable; + core.UnEquipWearableEvent -= OnUnEquipWearable; + core.UnEquipEmoteEvent -= OnUnEquipEmote; + core.EmoteSlotSelectEvent -= OnEmoteSlotSelect; + core.SelectEmoteEvent -= OnSelectEmote; + core.ForceRenderEvent -= OnForceRender; + core.FilterCategoryEvent -= OnFilterCategory; + core.SearchEvent -= OnSearch; + core.ChangedBackpackSectionEvent -= OnChangedBackpackSection; + core.ChangeColorEvent -= OnChangeColor; + core.UnEquipAllEvent -= OnUnEquipAll; + core.PublishProfileEvent -= OnPublishProfile; + core.FilterCategoryByEnumEvent -= OnFilterCategoryByEnum; + core.DeactivateEvent -= OnDeactivate; + } + + private void ReEmitWithAnalytics(int slot, IEmote emote, bool manuallyEquipped) + { + EquipEmoteEvent?.Invoke(slot, emote, manuallyEquipped); + + if (!manuallyEquipped) return; + + var emoteUrn = emote.GetUrn().ToString(); + analytics.Track(AnalyticsEvents.Wearables.USED_EMOTE, new JsonObject + { + { "item_id", emoteUrn }, // Id of the item - + { "is_base", !Emote.IsOnChain(emoteUrn) }, + { "name", emote.GetName() }, + { "emote_index", slot }, + { "source", "backpack" }, + }); + } + + private void OnSelectWearable(IWearable wearable) => SelectWearableEvent?.Invoke(wearable); + private void OnEquipWearable(IWearable wearable) => EquipWearableEvent?.Invoke(wearable); + private void OnUnEquipWearable(IWearable wearable) => UnEquipWearableEvent?.Invoke(wearable); + private void OnUnEquipEmote(int slot, IEmote emote) => UnEquipEmoteEvent?.Invoke(slot, emote); + private void OnEmoteSlotSelect(int slot) => EmoteSlotSelectEvent?.Invoke(slot); + private void OnSelectEmote(IEmote emote) => SelectEmoteEvent?.Invoke(emote); + private void OnForceRender(IReadOnlyCollection forceRender) => ForceRenderEvent?.Invoke(forceRender); + private void OnFilterCategory(string category) => FilterCategoryEvent?.Invoke(category); + private void OnSearch(string searchText) => SearchEvent?.Invoke(searchText); + private void OnChangedBackpackSection(BackpackSections section) => ChangedBackpackSectionEvent?.Invoke(section); + private void OnChangeColor(Color color, string category) => ChangeColorEvent?.Invoke(color, category); + private void OnUnEquipAll() => UnEquipAllEvent?.Invoke(); + private void OnPublishProfile() => PublishProfileEvent?.Invoke(); + private void OnFilterCategoryByEnum(AvatarWearableCategoryEnum categoryEnum) => FilterCategoryByEnumEvent?.Invoke(categoryEnum); + private void OnDeactivate() => DeactivateEvent?.Invoke(); + + public void SendUnEquipAll() => + core.SendUnEquipAll(); + + public void SendChangeColor(Color newColor, string category) => + core.SendChangeColor(newColor, category); + + public void SendForceRender(IReadOnlyCollection forceRender) => + core.SendForceRender(forceRender); + + public void SendSearch(string searchText) => + core.SendSearch(searchText); + + public void SendPublishProfile() => + core.SendPublishProfile(); + + public void SendEmoteSlotSelect(int slot) => + core.SendEmoteSlotSelect(slot); + + public void SendBackpackDeactivateEvent() => + core.SendBackpackDeactivateEvent(); + + public void SendChangedBackpackSectionEvent(BackpackSections backpackSections) => + core.SendChangedBackpackSectionEvent(backpackSections); + + public void SendEmoteSelect(IEmote emote) => + core.SendEmoteSelect(emote); + + public void SendEquipEmote(int slot, IEmote emote, bool isManuallyEquipped) => + core.SendEquipEmote(slot, emote, isManuallyEquipped); + + public void SendUnEquipEmote(int slot, IEmote? emote) => + core.SendUnEquipEmote(slot, emote); + + public void SendFilterCategory(string category, AvatarWearableCategoryEnum categoryEnum) => + core.SendFilterCategory(category, categoryEnum); + + public void SendUnEquipWearable(IWearable unEquipWearable) => + core.SendUnEquipWearable(unEquipWearable); + + public void SendEquipWearable(IWearable equipWearable) => + core.SendEquipWearable(equipWearable); + + public void SendWearableSelect(IWearable equipWearable) => + core.SendWearableSelect(equipWearable); + } +} diff --git a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/DecoratorBased/BackpackEventBusDecorator.cs.meta b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/DecoratorBased/BackpackEventBusDecorator.cs.meta new file mode 100644 index 0000000000..a75e1efdac --- /dev/null +++ b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/DecoratorBased/BackpackEventBusDecorator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d289431f03664201b10044bed01efc6e +timeCreated: 1722515517 \ No newline at end of file diff --git a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/StaticCommonTraitsPlugin.cs b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/StaticCommonTraitsPlugin.cs index b656a4a4db..f37aeda8a0 100644 --- a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/StaticCommonTraitsPlugin.cs +++ b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/StaticCommonTraitsPlugin.cs @@ -10,6 +10,8 @@ public class StaticCommonTraitsPlugin : EventPlugin private readonly string sessionID = SystemInfo.deviceUniqueIdentifier + DateTime.Now.ToString("yyyyMMddHHmmssfff"); private readonly string rendererVersion = Application.version; private readonly string runtime = Application.isEditor? "editor" : Debug.isDebugBuild ? "debug" : "release"; + private readonly string os = SystemInfo.operatingSystem; + public override PluginType Type => PluginType.Enrichment; public override TrackEvent Track(TrackEvent trackEvent) @@ -18,6 +20,7 @@ public override TrackEvent Track(TrackEvent trackEvent) trackEvent.Context["session_id"] = sessionID; trackEvent.Context["renderer_version"] = rendererVersion; trackEvent.Context["runtime"] = runtime; + trackEvent.Context["operating_system"] = os; return trackEvent; } diff --git a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/AnalyticsEmotesSystem.cs b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/AnalyticsEmotesSystem.cs new file mode 100644 index 0000000000..e0837a04d7 --- /dev/null +++ b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/AnalyticsEmotesSystem.cs @@ -0,0 +1,65 @@ +using Arch.Core; +using Arch.SystemGroups; +using Arch.SystemGroups.DefaultSystemGroups; +using DCL.AvatarRendering.AvatarShape; +using DCL.AvatarRendering.Emotes; +using DCL.Diagnostics; +using DCL.PerformanceAndDiagnostics.Analytics; +using ECS; +using ECS.Abstract; +using Segment.Serialization; + +namespace DCL.Analytics.Systems +{ + [LogCategory(ReportCategory.ANALYTICS)] + [UpdateInGroup(typeof(PresentationSystemGroup))] + [UpdateAfter(typeof(AvatarGroup))] + [UpdateBefore(typeof(CharacterEmoteSystem))] + public partial class AnalyticsEmotesSystem : BaseUnityLoopSystem + { + private readonly IAnalyticsController analytics; + + private readonly IRealmData realmData; + private readonly Entity playerEntity; + + private string lastEmoteId = string.Empty; + + public AnalyticsEmotesSystem(World world, IAnalyticsController analytics, IRealmData realmData, in Entity playerEntity) : base(world) + { + this.analytics = analytics; + this.realmData = realmData; + this.playerEntity = playerEntity; + } + + protected override void Update(float t) + { + if (!realmData.Configured) return; + + if (World.TryGet(playerEntity, out var emoteIntent)) + { + if (emoteIntent.TriggerSource is TriggerSource.REMOTE or TriggerSource.PREVIEW) + return; + + if (lastEmoteId != emoteIntent.EmoteId) + { + lastEmoteId = emoteIntent.EmoteId; + SendAnalytics(emoteIntent.EmoteId, !Emote.IsOnChain(emoteIntent.EmoteId), emoteIntent.TriggerSource.ToString().ToLower()); + } + } + else + lastEmoteId = string.Empty; + } + + private void SendAnalytics(string id, bool isBase, string source) + { + analytics.Track(AnalyticsEvents.Wearables.USED_EMOTE, new JsonObject + { + { "item_id", id }, // Id of the item - + { "is_base", isBase }, // if the item is a base emote + { "name", string.Empty }, + { "emote_index", -1 }, // Index where the emote was placed if the user equipped an emote. + { "source", source }, + }); + } + } +} diff --git a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/AnalyticsEmotesSystem.cs.meta b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/AnalyticsEmotesSystem.cs.meta new file mode 100644 index 0000000000..efb3d76d62 --- /dev/null +++ b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/AnalyticsEmotesSystem.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f997b4f5e5ee4a809334a811e3602659 +timeCreated: 1722510778 \ No newline at end of file diff --git a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/PlayerParcelChangedAnalyticsSystem.cs b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/PlayerParcelChangedAnalyticsSystem.cs index 4a5aa8a112..d67f30f3ff 100644 --- a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/PlayerParcelChangedAnalyticsSystem.cs +++ b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/PlayerParcelChangedAnalyticsSystem.cs @@ -2,6 +2,7 @@ using Arch.SystemGroups; using Arch.SystemGroups.DefaultSystemGroups; using DCL.Character.Components; +using DCL.Diagnostics; using DCL.PerformanceAndDiagnostics.Analytics; using ECS; using ECS.Abstract; @@ -13,6 +14,7 @@ namespace DCL.Analytics.Systems { + [LogCategory(ReportCategory.ANALYTICS)] [UpdateInGroup(typeof(PostRenderingSystemGroup))] public partial class PlayerParcelChangedAnalyticsSystem : BaseUnityLoopSystem { diff --git a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/TimeSpentInWorldAnalyticsSystem.cs b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/TimeSpentInWorldAnalyticsSystem.cs index 29140fbcfc..3ba69dbeaf 100644 --- a/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/TimeSpentInWorldAnalyticsSystem.cs +++ b/Explorer/Assets/DCL/PerformanceAndDiagnostics/Analytics/Systems/TimeSpentInWorldAnalyticsSystem.cs @@ -1,6 +1,7 @@ using Arch.Core; using Arch.SystemGroups; using Arch.SystemGroups.DefaultSystemGroups; +using DCL.Diagnostics; using DCL.PerformanceAndDiagnostics.Analytics; using ECS; using ECS.Abstract; @@ -10,6 +11,7 @@ namespace DCL.Analytics.Systems { + [LogCategory(ReportCategory.ANALYTICS)] [UpdateInGroup(typeof(PostRenderingSystemGroup))] public partial class TimeSpentInWorldAnalyticsSystem : BaseUnityLoopSystem { diff --git a/Explorer/Assets/DCL/PluginSystem/Global/AnalyticsPlugin.cs b/Explorer/Assets/DCL/PluginSystem/Global/AnalyticsPlugin.cs index b9a0616d2a..0f00f1904b 100644 --- a/Explorer/Assets/DCL/PluginSystem/Global/AnalyticsPlugin.cs +++ b/Explorer/Assets/DCL/PluginSystem/Global/AnalyticsPlugin.cs @@ -39,6 +39,7 @@ public void InjectToWorld(ref ArchSystemsWorldBuilder builder, PerformanceAnalyticsSystem.InjectToWorld(ref builder, analytics, realmData, profiler); TimeSpentInWorldAnalyticsSystem.InjectToWorld(ref builder, analytics, realmData); BadgesHeightReachedSystem.InjectToWorld(ref builder, analytics, realmData, arguments.PlayerEntity); + AnalyticsEmotesSystem.InjectToWorld(ref builder, analytics, realmData, arguments.PlayerEntity); } diff --git a/Explorer/Assets/DCL/PluginSystem/Global/BackpackSubPlugin.cs b/Explorer/Assets/DCL/PluginSystem/Global/BackpackSubPlugin.cs index 58cb6a28e4..42719e0fcf 100644 --- a/Explorer/Assets/DCL/PluginSystem/Global/BackpackSubPlugin.cs +++ b/Explorer/Assets/DCL/PluginSystem/Global/BackpackSubPlugin.cs @@ -38,7 +38,7 @@ internal class BackpackSubPlugin : IDisposable private readonly DCLInput dclInput; private readonly IWeb3IdentityCache web3Identity; private readonly BackpackCommandBus backpackCommandBus; - private readonly BackpackEventBus backpackEventBus; + private readonly IBackpackEventBus backpackEventBus; private readonly ICharacterPreviewFactory characterPreviewFactory; private readonly BackpackEquipStatusController backpackEquipStatusController; private readonly URLDomain assetBundleURL; @@ -66,7 +66,8 @@ public BackpackSubPlugin( DCLInput dclInput, URLDomain assetBundleURL, IWebRequestController webRequestController, - CharacterPreviewEventBus characterPreviewEventBus) + CharacterPreviewEventBus characterPreviewEventBus, + IBackpackEventBus backpackEventBus) { this.assetsProvisioner = assetsProvisioner; this.web3Identity = web3Identity; @@ -83,7 +84,7 @@ public BackpackSubPlugin( this.characterPreviewEventBus = characterPreviewEventBus; backpackCommandBus = new BackpackCommandBus(); - backpackEventBus = new BackpackEventBus(); + this.backpackEventBus = backpackEventBus; backpackEquipStatusController = new BackpackEquipStatusController( backpackEventBus, diff --git a/Explorer/Assets/DCL/PluginSystem/Global/ExplorePanelPlugin.cs b/Explorer/Assets/DCL/PluginSystem/Global/ExplorePanelPlugin.cs index 914a09c134..99289d2bfd 100644 --- a/Explorer/Assets/DCL/PluginSystem/Global/ExplorePanelPlugin.cs +++ b/Explorer/Assets/DCL/PluginSystem/Global/ExplorePanelPlugin.cs @@ -7,6 +7,7 @@ using DCL.AvatarRendering.Wearables.Equipped; using DCL.AvatarRendering.Wearables.Helpers; using DCL.Backpack; +using DCL.Backpack.BackpackBus; using DCL.Browser; using DCL.CharacterPreview; using DCL.ExplorePanel; @@ -60,6 +61,8 @@ public class ExplorePanelPlugin : DCLGlobalPluginBase forceRender; private ExplorePanelInputHandler? inputHandler; @@ -96,7 +99,8 @@ public ExplorePanelPlugin(IAssetsProvisioner assetsProvisioner, URLDomain assetBundleURL, INotificationsBusController notificationsBusController, CharacterPreviewEventBus characterPreviewEventBus, - IMapPathEventBus mapPathEventBus) + IMapPathEventBus mapPathEventBus, + IBackpackEventBus backpackEventBus) { this.assetsProvisioner = assetsProvisioner; this.mvcManager = mvcManager; @@ -123,6 +127,7 @@ public ExplorePanelPlugin(IAssetsProvisioner assetsProvisioner, this.dclInput = dclInput; this.characterPreviewEventBus = characterPreviewEventBus; this.mapPathEventBus = mapPathEventBus; + this.backpackEventBus = backpackEventBus; } public override void Dispose() @@ -150,7 +155,8 @@ public override void Dispose() dclInput, assetBundleURL, webRequestController, - characterPreviewEventBus + characterPreviewEventBus, + backpackEventBus ); ExplorePanelView panelViewAsset = (await assetsProvisioner.ProvideMainAssetValueAsync(settings.ExplorePanelPrefab, ct: ct)).GetComponent(); diff --git a/Explorer/Assets/Scripts/CrdtEcsBridge/RestrictedActions/GlobalWorldActions.cs b/Explorer/Assets/Scripts/CrdtEcsBridge/RestrictedActions/GlobalWorldActions.cs index bb82ff7248..d91a99a497 100644 --- a/Explorer/Assets/Scripts/CrdtEcsBridge/RestrictedActions/GlobalWorldActions.cs +++ b/Explorer/Assets/Scripts/CrdtEcsBridge/RestrictedActions/GlobalWorldActions.cs @@ -68,7 +68,7 @@ public async UniTask TriggerSceneEmoteAsync(string sceneId, SceneAssetBundleMani public void TriggerEmote(URN urn) { - world.Add(playerEntity, new CharacterEmoteIntent { EmoteId = urn, Spatial = true }); + world.Add(playerEntity, new CharacterEmoteIntent { EmoteId = urn, Spatial = true, TriggerSource = TriggerSource.SCENE}); } } } diff --git a/Explorer/Assets/Scripts/Global/Dynamic/DynamicWorldContainer.cs b/Explorer/Assets/Scripts/Global/Dynamic/DynamicWorldContainer.cs index b7f06ff394..787d21fa43 100644 --- a/Explorer/Assets/Scripts/Global/Dynamic/DynamicWorldContainer.cs +++ b/Explorer/Assets/Scripts/Global/Dynamic/DynamicWorldContainer.cs @@ -8,6 +8,7 @@ using DCL.AvatarRendering.Wearables; using DCL.AvatarRendering.Wearables.Equipped; using DCL.AvatarRendering.Wearables.Helpers; +using DCL.Backpack.BackpackBus; using DCL.Browser; using DCL.CharacterPreview; using DCL.Chat; @@ -332,13 +333,20 @@ async UniTask InitializeContainersAsync(IPluginSettingsContainer settingsContain { ReloadSceneChatCommand.REGEX, () => new ReloadSceneChatCommand(container.reloadSceneController) }, }; - IChatMessagesBus chatMessageBus = new MultiplayerChatMessagesBus(container.MessagePipesHub, container.ProfileRepository, new MessageDeduplication()) + IChatMessagesBus coreChatMessageBus = new MultiplayerChatMessagesBus(container.MessagePipesHub, container.ProfileRepository, new MessageDeduplication()) .WithSelfResend(identityCache, container.ProfileRepository) .WithIgnoreSymbols() .WithCommands(chatCommandsFactory) .WithDebugPanel(debugBuilder); - container.ChatMessagesBus = dynamicWorldParams.EnableAnalytics ? new ChatMessagesBusAnalyticsDecorator(chatMessageBus, bootstrapContainer.Analytics!) : chatMessageBus; + container.ChatMessagesBus = dynamicWorldParams.EnableAnalytics + ? new ChatMessagesBusAnalyticsDecorator(coreChatMessageBus, bootstrapContainer.Analytics!) + : coreChatMessageBus; + + var coreBackpackEventBus = new BackpackEventBus(); + IBackpackEventBus backpackEventBus = dynamicWorldParams.EnableAnalytics + ? new BackpackEventBusAnalyticsDecorator(coreBackpackEventBus, bootstrapContainer.Analytics!) + : coreBackpackEventBus; if (!string.IsNullOrEmpty(dynamicWorldParams.LocalSceneDevelopmentRealm)) container.localSceneDevelopmentController = new LocalSceneDevelopmentController(container.reloadSceneController, dynamicWorldParams.LocalSceneDevelopmentRealm); @@ -450,7 +458,8 @@ async UniTask InitializeContainersAsync(IPluginSettingsContainer settingsContain assetBundlesURL, notificationsBusController, characterPreviewEventBus, - mapPathEventBus + mapPathEventBus, + backpackEventBus ), new CharacterPreviewPlugin(staticContainer.ComponentsContainer.ComponentPoolsRegistry, assetsProvisioner, staticContainer.CacheCleaner), new WebRequestsPlugin(staticContainer.WebRequestsContainer.AnalyticsContainer, debugBuilder),