Skip to content

Commit

Permalink
fix: emotes spam stuck (#2573)
Browse files Browse the repository at this point in the history
  • Loading branch information
lorux0 authored Oct 25, 2024
1 parent a7b1dd4 commit 79806f3
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using CommunicationData.URLHelpers;
using Cysharp.Threading.Tasks;
using DCL.AvatarRendering.Loading.Components;
using DCL.Web3;
using Global.AppArgs;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace DCL.AvatarRendering.Emotes
{
public class ApplicationParamsEmoteProvider : IEmoteProvider
{
private readonly IAppArgs appArgs;
private readonly IEmoteProvider source;

public ApplicationParamsEmoteProvider(IAppArgs appArgs,
IEmoteProvider source)
{
this.appArgs = appArgs;
this.source = source;
}

public async UniTask<int> GetOwnedEmotesAsync(Web3Address userId, CancellationToken ct,
IEmoteProvider.OwnedEmotesRequestOptions requestOptions,
List<IEmote> output)
{
if (!appArgs.TryGetValue("self-preview-emotes", out string? emotesCsv))
return await source.GetOwnedEmotesAsync(userId, ct, requestOptions, output);

URN[] pointers = emotesCsv!.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(s => new URN(s))
.ToArray();

await UniTask.WhenAll(GetEmotesAsync(pointers, BodyShape.MALE, ct, output),
GetEmotesAsync(pointers, BodyShape.FEMALE, ct, output));

return output.Count;
}

public UniTask GetEmotesAsync(IReadOnlyCollection<URN> emoteIds, BodyShape bodyShape, CancellationToken ct, List<IEmote> output) =>
source.GetEmotesAsync(emoteIds, bodyShape, ct, output);
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ namespace DCL.AvatarRendering.Wearables
{
public class ApplicationParametersWearablesProvider : IWearablesProvider
{
private readonly IAppArgs applicationParametersParser;
private readonly IAppArgs appArgs;
private readonly IWearablesProvider source;
private readonly World world;
private readonly string[] allWearableCategories = WearablesConstants.CATEGORIES_PRIORITY.ToArray();
private readonly List<IWearable> resultWearablesBuffer = new ();

public ApplicationParametersWearablesProvider(IAppArgs applicationParametersParser,
public ApplicationParametersWearablesProvider(IAppArgs appArgs,
IWearablesProvider source,
World world)
{
this.applicationParametersParser = applicationParametersParser;
this.appArgs = appArgs;
this.source = source;
this.world = world;
}
Expand All @@ -41,14 +41,18 @@ public ApplicationParametersWearablesProvider(IAppArgs applicationParametersPars
string? name = null,
List<IWearable>? results = null)
{
if (!applicationParametersParser.TryGetValue("self-preview-wearables", out string? wearablesCsv))
if (!appArgs.TryGetValue("self-preview-wearables", out string? wearablesCsv))
return await source.GetAsync(pageSize, pageNumber, ct, sortingField, orderBy, category, collectionType, name, results);

URN[] pointers = wearablesCsv!.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(s => new URN(s)).ToArray();
.Select(s => new URN(s))
.ToArray();

IReadOnlyCollection<IWearable>? maleWearables = await RequestPointersAsync(pointers, BodyShape.MALE, ct);
IReadOnlyCollection<IWearable>? femaleWearables = await RequestPointersAsync(pointers, BodyShape.FEMALE, ct);
(IReadOnlyCollection<IWearable>? maleWearables, IReadOnlyCollection<IWearable>? femaleWearables) =
await UniTask.WhenAll(RequestPointersAsync(pointers, BodyShape.MALE, ct),
RequestPointersAsync(pointers, BodyShape.FEMALE, ct));

results ??= new List<IWearable>();

lock (resultWearablesBuffer)
{
Expand All @@ -61,7 +65,8 @@ public ApplicationParametersWearablesProvider(IAppArgs applicationParametersPars
resultWearablesBuffer.AddRange(femaleWearables);

int pageIndex = pageNumber - 1;
return (resultWearablesBuffer.Skip(pageIndex * pageSize).Take(pageSize).ToArray(), resultWearablesBuffer.Count);
results.AddRange(resultWearablesBuffer.Skip(pageIndex * pageSize).Take(pageSize));
return (results, resultWearablesBuffer.Count);
}
}

Expand All @@ -70,6 +75,7 @@ public ApplicationParametersWearablesProvider(IAppArgs applicationParametersPars
CancellationToken ct)
{
var promise = WearablePromise.Create(world,

// We pass all categories as force renderer to force the download of all of them
// Otherwise they will be skipped if any wearable is hiding the category
WearableComponentsUtils.CreateGetWearablesByPointersIntention(bodyShape, pointers, allWearableCategories),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ private void OnColorChange(Color newColor, string category)
previewAvatarModel.SkinColor = newColor;
break;
}

OnModelUpdated();
}

Expand Down Expand Up @@ -180,14 +181,18 @@ async UniTaskVoid EnsureEmoteAndPlayItAsync(CancellationToken ct)
{
URN urn = emote.GetUrn().Shorten();

// In case you want to preview an emote, it might happen that the asset bundles are not loaded
// By adding the emote we force to fetch them if missing
// Ensure assets are loaded for the emote
if (!previewAvatarModel.Emotes?.Contains(urn) ?? true)
{
previewAvatarModel.Emotes!.Add(urn);
await ShowLoadingSpinnerAndUpdateAvatarAsync(ct);
// Remove the emote so it stays original
previewAvatarModel.Emotes!.Remove(urn);

try { await ShowLoadingSpinnerAndUpdateAvatarAsync(ct); }
catch (OperationCanceledException) { }
finally
{
// Remove the emote so it stays original
previewAvatarModel.Emotes!.Remove(urn);
}
}

PlayEmote(urn);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public void Dispose()

public UniTask UpdateAvatarAsync(CharacterPreviewAvatarModel avatarModel, CancellationToken ct)
{
ct.ThrowIfCancellationRequested();

ref AvatarShapeComponent avatarShape = ref globalWorld.Get<AvatarShapeComponent>(characterPreviewEntity);

avatarShape.SkinColor = avatarModel.SkinColor;
Expand All @@ -87,25 +89,50 @@ public UniTask UpdateAvatarAsync(CharacterPreviewAvatarModel avatarModel, Cancel
PartitionComponent.TOP_PRIORITY
);

globalWorld.Create(EmotePromise.Create(globalWorld,
Entity emotePromiseEntity = globalWorld.Create(EmotePromise.Create(globalWorld,
EmoteComponentsUtils.CreateGetEmotesByPointersIntention(avatarShape.BodyShape,
avatarModel.Emotes ?? (IReadOnlyCollection<URN>)Array.Empty<URN>()),
PartitionComponent.TOP_PRIORITY));

EntityReference emotePromiseRef = globalWorld.Reference(emotePromiseEntity);

avatarShape.IsDirty = true;

return WaitForAvatarInstantiatedAsync(ct);
return WaitForAvatarInstantiatedAsync(emotePromiseRef, ct);
}

private async UniTask WaitForAvatarInstantiatedAsync(CancellationToken ct)
private async UniTask WaitForAvatarInstantiatedAsync(EntityReference emotePromiseEntity, CancellationToken ct)
{
while (!ct.IsCancellationRequested && globalWorld.Get<AvatarShapeComponent>(characterPreviewEntity).IsDirty)
World world = globalWorld;
Entity avatarEntity = characterPreviewEntity;

while (!IsAvatarLoaded() || !IsEmoteLoaded())
await UniTask.Yield(ct);

ct.ThrowIfCancellationRequested();

return;

bool IsAvatarLoaded()
{
return !world.Get<AvatarShapeComponent>(avatarEntity).IsDirty;
}

bool IsEmoteLoaded()
{
return !emotePromiseEntity.IsAlive(world)
|| world.Get<EmotePromise>(emotePromiseEntity).IsConsumed;
}
}

public void PlayEmote(string emoteId)
{
globalWorld.Add(characterPreviewEntity, new CharacterEmoteIntent { EmoteId = emoteId, TriggerSource = TriggerSource.PREVIEW});
var intent = new CharacterEmoteIntent { EmoteId = emoteId, TriggerSource = TriggerSource.PREVIEW };

if (globalWorld.Has<CharacterEmoteIntent>(characterPreviewEntity))
globalWorld.Set(characterPreviewEntity, intent);
else
globalWorld.Add(characterPreviewEntity, intent);
}

public void StopEmotes()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,18 @@ private void OnAnyCharacterPreviewHide(CharacterPreviewControllerBase characterP
protected void OnModelUpdated()
{
updateModelCancellationToken = updateModelCancellationToken.SafeRestart();
ShowLoadingSpinnerAndUpdateAvatarAsync(updateModelCancellationToken.Token).Forget();

UpdateModelAsync(updateModelCancellationToken.Token).Forget();
return;

async UniTaskVoid UpdateModelAsync(CancellationToken ct)
{
try
{
await ShowLoadingSpinnerAndUpdateAvatarAsync(ct);
}
catch (OperationCanceledException) { }
}
}

protected async UniTask ShowLoadingSpinnerAndUpdateAvatarAsync(CancellationToken ct)
Expand All @@ -232,7 +243,6 @@ protected async UniTask ShowLoadingSpinnerAndUpdateAvatarAsync(CancellationToken
DisableSpinner(spinner);
}


private void DisableSpinner(GameObject spinner)
{
spinner.SetActive(false);
Expand All @@ -250,11 +260,8 @@ private GameObject EnableSpinner()
return spinner;
}

private async UniTask UpdateAvatarAsync(CharacterPreviewAvatarModel model, CancellationToken ct)
{
try { await (previewController?.UpdateAvatarAsync(model, ct) ?? UniTask.CompletedTask); }
catch (OperationCanceledException) { }
}
private async UniTask UpdateAvatarAsync(CharacterPreviewAvatarModel model, CancellationToken ct) =>
await (previewController?.UpdateAvatarAsync(model, ct) ?? UniTask.CompletedTask);

protected void StopEmotes() =>
previewController?.StopEmotes();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ IMultiPool MultiPoolFactory() =>
var selfProfile = new SelfProfile(container.ProfileRepository, identityCache, equippedWearables, wearableCatalog,
emotesCache, equippedEmotes, forceRender, selfEmotes);

IEmoteProvider emoteProvider = new EcsEmoteProvider(globalWorld, staticContainer.RealmData);
IEmoteProvider emoteProvider = new ApplicationParamsEmoteProvider(appArgs,
new EcsEmoteProvider(globalWorld, staticContainer.RealmData));

container.wearablesProvider = new ApplicationParametersWearablesProvider(appArgs,
new ECSWearablesProvider(identityCache, globalWorld),
Expand Down

0 comments on commit 79806f3

Please sign in to comment.