diff --git a/.github/workflows/build-unitycloud.yml b/.github/workflows/build-unitycloud.yml index 96aa1dcd0f6..88d69448425 100644 --- a/.github/workflows/build-unitycloud.yml +++ b/.github/workflows/build-unitycloud.yml @@ -398,7 +398,7 @@ jobs: strategy: fail-fast: false matrix: - target: ['windows64', 'macos'] + target: ['windows64', 'macos', 'web'] steps: - name: Checkout code uses: actions/checkout@v4 @@ -493,6 +493,18 @@ jobs: !build/**/*.pdb if-no-files-found: error + - name: Upload artifact for Web + id: upload-web-artifact + if: matrix.target == 'web' + uses: actions/upload-artifact@v4 + with: + name: ${{ env.artifact_name }} + path: | + build + !build/**/*_BackUpThisFolder_ButDontShipItWithYourGame + !build/**/*_BurstDebugInformation_DoNotShip + if-no-files-found: error + - name: Compress the build folder to upload it to S3 run: | mkdir upload_to_s3/ && \ @@ -501,6 +513,8 @@ jobs: elif [ "${{ matrix.target }}" == "windows64" ]; then cd build zip -r ../upload_to_s3/${{ env.artifact_name }}.zip . -x "*_BackUpThisFolder_ButDontShipItWithYourGame**" -x "*_BurstDebugInformation_DoNotShip**" -x "*.pdb" + elif [ "${{ matrix.target }}" == "web" ]; then + cp -r build upload_to_s3/build fi - name: Install jq @@ -508,6 +522,7 @@ jobs: - name: Enforce artifact size budget id: size_check + if: matrix.target != 'web' env: # Per-target absolute caps (MB) MAX_MACOS_MB: 450 @@ -672,17 +687,29 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.EXPLORER_TEAM_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.EXPLORER_TEAM_AWS_SECRET_ACCESS_KEY }} EXPLORER_TEAM_S3_BUCKET: ${{ secrets.EXPLORER_TEAM_S3_BUCKET }} - DESTINATION_PATH: "${{ - inputs.is_release_build && format('@dcl/{0}/releases/{1}', github.event.repository.name, inputs.tag_version) - || format('@dcl/{0}/branch/{1}/{2}-{3}-{4}', github.event.repository.name, env.SAFE_BRANCH_NAME, env.BUILD_PREFIX, github.run_number, env.SHA_SHORT) - }}" + EXPLORER_TEAM_S3_BUCKET_PUBLIC_URL: ${{ vars.EXPLORER_TEAM_S3_BUCKET_PUBLIC_URL }} run: | + set -euo pipefail + + DESTINATION_PATH="${{ inputs.is_release_build && format('@dcl/{0}/releases/{1}', github.event.repository.name, inputs.tag_version) || format('@dcl/{0}/branch/{1}/{2}-{3}-{4}', github.event.repository.name, env.SAFE_BRANCH_NAME, env.BUILD_PREFIX, github.run_number, env.SHA_SHORT) }}" + + # Latest dev build for webgl + if [ "${{ matrix.target }}" == "web" ] && [ "${GITHUB_REF_NAME}" == "dev" ]; then + DESTINATION_PATH="@dcl/${{ github.event.repository.name }}/dev/webgl-latest" + fi + + if [ "${{ matrix.target }}" == "web" ]; then + echo "Web build link: $EXPLORER_TEAM_S3_BUCKET_PUBLIC_URL/$DESTINATION_PATH/build/Decentraland/index.html" + fi + npx @dcl/cdn-uploader@next \ --bucket $EXPLORER_TEAM_S3_BUCKET \ --local-folder upload_to_s3 \ --bucket-folder $DESTINATION_PATH + # web doesn't have debug symbols - name: Upload debug symbols + if: matrix.target != 'web' uses: actions/upload-artifact@v4 with: name: ${{ env.artifact_name }}_debug_symbols @@ -692,7 +719,7 @@ jobs: if-no-files-found: error - name: Upload debug symbols to Sentry - if: ${{ needs.prebuild.outputs.sentry_enabled == 'true' }} + if: ${{ needs.prebuild.outputs.sentry_enabled == 'true' && matrix.target != 'web' }} shell: bash env: SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_CLI_AUTH_TOKEN }} diff --git a/.github/workflows/pr-comment-artifact-url.yml b/.github/workflows/pr-comment-artifact-url.yml index 12601ce1278..31332b68811 100644 --- a/.github/workflows/pr-comment-artifact-url.yml +++ b/.github/workflows/pr-comment-artifact-url.yml @@ -122,6 +122,15 @@ jobs: echo "Mac Artifact ID: $MAC_ARTIFACT_ID" echo "MAC_ARTIFACT_ID=$MAC_ARTIFACT_ID" >> "$GITHUB_ENV" + WEB_ARTIFACT_ID=$(gh api "/repos/$OWNER/$REPO/actions/artifacts" \ + --jq ".artifacts.[] | + select(.workflow_run.id==${PREVIOUS_JOB_ID}) | + select(.expired==false) | + select(.name==\"Decentraland_web\") | + .id") + echo "Web Artifact ID: $WEB_ARTIFACT_ID" + echo "WEB_ARTIFACT_ID=$WEB_ARTIFACT_ID" >> "$GITHUB_ENV" + ORIGINAL_EVENT=$(jq -r '.event' <<< "$WORKFLOW_RUN_EVENT_OBJ") echo "Original Event: $ORIGINAL_EVENT" @@ -208,6 +217,8 @@ jobs: WINDOWS_ARTIFACT_S3_URL: "${{ format('{0}/{1}/Decentraland_windows64.zip', vars.EXPLORER_TEAM_S3_BUCKET_PUBLIC_URL, env.ARTIFACT_S3_DESTINATION_PATH) }}" MAC_ARTIFACT_URL: "${{ github.server_url }}/${{ github.repository }}/suites/${{ env.SUITE_ID }}/artifacts/${{ env.MAC_ARTIFACT_ID }}" MAC_ARTIFACT_S3_URL: "${{ format('{0}/{1}/Decentraland_macos.zip', vars.EXPLORER_TEAM_S3_BUCKET_PUBLIC_URL, env.ARTIFACT_S3_DESTINATION_PATH) }}" + WEB_ARTIFACT_URL: "${{ github.server_url }}/${{ github.repository }}/suites/${{ env.SUITE_ID }}/artifacts/${{ env.WEB_ARTIFACT_ID }}" + WEB_ARTIFACT_S3_URL: "${{ format('{0}/{1}/build/Decentraland/index.html', vars.EXPLORER_TEAM_S3_BUCKET_PUBLIC_URL, env.ARTIFACT_S3_DESTINATION_PATH) }}" HEAD_SHA: "${{ env.HEAD_SHA }}" uses: peter-evans/create-or-update-comment@v3 with: @@ -217,7 +228,7 @@ jobs: body: |- ![badge] - Windows and Mac build successful in Unity Cloud! You can find a link to the downloadable artifact below. + Windows, Mac and Web build successful in Unity Cloud! You can find a link to the downloadable artifact below. | Name | Link | | -------- | ----------------------- | @@ -227,6 +238,8 @@ jobs: | Download Windows S3 | ${{ env.WINDOWS_ARTIFACT_S3_URL }} | | Download Mac | ${{ env.MAC_ARTIFACT_URL }} | | Download Mac S3 | ${{ env.MAC_ARTIFACT_S3_URL }} | + | Download Web | ${{ env.WEB_ARTIFACT_URL }} | + | Web Build on S3 | ${{ env.WEB_ARTIFACT_S3_URL }} | | Built on | ${{ env.BUILD_DATE }} | ${{ env.SIZE_REPORT }} diff --git a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/ClientWebSocketApiImplementation.cs b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/ClientWebSocketApiImplementation.cs index 297c7c20685..6aaeecdcae1 100644 --- a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/ClientWebSocketApiImplementation.cs +++ b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/ClientWebSocketApiImplementation.cs @@ -133,7 +133,7 @@ public async UniTask CloseAsync(int websocketId, CancellationToken ct) text = Encoding.UTF8.GetString(result.Span), }; - var binary = jsOperations.NewUint8Array(result.Length); + IDCLTypedArray binary = jsOperations.NewUint8Array(result.Length); binary.WriteBytes(result.Array, 0ul, (ulong)result.Length, 0ul); return new IWebSocketApi.ReceiveResponse diff --git a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Communications/CommunicationsControllerAPIImplementationBase.cs b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Communications/CommunicationsControllerAPIImplementationBase.cs index 1ce8766513a..08944408cb8 100644 --- a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Communications/CommunicationsControllerAPIImplementationBase.cs +++ b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Communications/CommunicationsControllerAPIImplementationBase.cs @@ -27,7 +27,7 @@ protected enum CommsMessageType { private readonly ISceneCommunicationPipe sceneCommunicationPipe; protected readonly IJsOperations jsOperations; private readonly ISceneCommunicationPipe.MsgType typeToHandle; - private readonly ScriptObject eventArray; + private readonly IDCLScriptObject eventArray; private readonly ISceneCommunicationPipe.SceneMessageHandler onMessageReceivedCached; private readonly ISceneData sceneData; @@ -105,15 +105,15 @@ public ScriptObject GetResult() for (var i = 0; i < eventsToProcess.Count; i++) { PoolableByteArray src = eventsToProcess[i]; - ITypedArray dst = jsOperations.GetTempUint8Array(); - dst.WriteBytes(src.Span, 0ul, (ulong)src.Length, 0ul); + IDCLTypedArray dst = jsOperations.GetTempUint8Array(); + dst.WriteBytes(src.Array, 0ul, (ulong)src.Length, 0ul); src.Dispose(); - object subArray = dst.InvokeMethod("subarray", 0, src.Length); + object subArray = ((IDCLScriptObject)dst).InvokeMethod("subarray", 0, src.Length); eventArray.SetProperty(i, subArray); } eventsToProcess.Clear(); - return eventArray; + return (ScriptObject)eventArray.GetNativeObject(); } } diff --git a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/EngineAPIImplementation.cs b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/EngineAPIImplementation.cs index 0db8fd810cf..195e6421e20 100644 --- a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/EngineAPIImplementation.cs +++ b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/EngineAPIImplementation.cs @@ -35,8 +35,10 @@ public class EngineAPIImplementation : IEngineApi private readonly CustomSampler deserializeBatchSampler; private readonly ISceneExceptionsHandler exceptionsHandler; private readonly IInstancePoolsProvider instancePoolsProvider; +#if !UNITY_WEBGL private readonly MultiThreadSync multiThreadSync; private readonly MultiThreadSync.Owner syncOwner; +#endif private readonly IOutgoingCRDTMessagesProvider outgoingCrtdMessagesProvider; private readonly CustomSampler outgoingMessagesSampler; private readonly ISystemGroupsUpdateGate systemGroupsUpdateGate; @@ -53,9 +55,12 @@ public EngineAPIImplementation( ICRDTWorldSynchronizer crdtWorldSynchronizer, IOutgoingCRDTMessagesProvider outgoingCrtdMessagesProvider, ISystemGroupsUpdateGate systemGroupsUpdateGate, - ISceneExceptionsHandler exceptionsHandler, - MultiThreadSync multiThreadSync, - MultiThreadSync.Owner syncOwner) + ISceneExceptionsHandler exceptionsHandler +#if !UNITY_WEBGL + ,MultiThreadSync multiThreadSync, + MultiThreadSync.Owner syncOwner +#endif + ) { sharedPoolsProvider = poolsProvider; this.instancePoolsProvider = instancePoolsProvider; @@ -64,8 +69,10 @@ public EngineAPIImplementation( this.crdtSerializer = crdtSerializer; this.crdtWorldSynchronizer = crdtWorldSynchronizer; this.outgoingCrtdMessagesProvider = outgoingCrtdMessagesProvider; +#if !UNITY_WEBGL this.multiThreadSync = multiThreadSync; this.syncOwner = syncOwner; +#endif this.systemGroupsUpdateGate = systemGroupsUpdateGate; this.exceptionsHandler = exceptionsHandler; @@ -248,7 +255,9 @@ private void ApplySyncCommandBuffer(IWorldSyncCommandBuffer worldSyncBuffer) { try { +#if !UNITY_WEBGL using MultiThreadSync.Scope mutex = multiThreadSync.GetScope(syncOwner); +#endif applyBufferSampler.Begin(); diff --git a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/RuntimeImplementation.cs b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/RuntimeImplementation.cs index 5fee645c047..7534978c8bf 100644 --- a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/RuntimeImplementation.cs +++ b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/RuntimeImplementation.cs @@ -11,7 +11,6 @@ using ECS.Prioritization.Components; using ECS.StreamableLoading.Common.Components; using ECS.StreamableLoading.Common.Systems; -using Microsoft.ClearScript.JavaScript; using Newtonsoft.Json; using SceneRunner.Scene; using SceneRuntime; @@ -63,7 +62,7 @@ public void Dispose() { } await UniTask.SwitchToMainThread(); - async UniTask>> CreateFileRequestAsync(SubIntention intention, IAcquiredBudget budget, IPartitionComponent partition, CancellationToken ct) + async UniTask>> CreateFileRequestAsync(SubIntention intention, IAcquiredBudget budget, IPartitionComponent partition, CancellationToken ct) { using DownloadHandler? downloadHandler = await webRequestController.GetAsync(intention.CommonArguments.URL, ct, ReportCategory.JAVASCRIPT).ExposeDownloadHandlerAsync(); NativeArray.ReadOnly nativeBytes = downloadHandler.nativeData; @@ -71,15 +70,15 @@ async UniTask>> CreateFileRequestAsync await DCLTask.SwitchToThreadPool(); // create script byte array - ITypedArray array = jsOperations.NewUint8Array(nativeBytes.Length); + IDCLTypedArray array = jsOperations.NewUint8Array(nativeBytes.Length); // transfer data to script byte array array.Write(nativeBytes, (ulong)nativeBytes.Length, 0); - return new StreamableLoadingResult>(array); + return new StreamableLoadingResult>(array); } var intent = new SubIntention(new CommonLoadingArguments(url)); - ITypedArray content = (await intent.RepeatLoopAsync(NoAcquiredBudget.INSTANCE, PartitionComponent.TOP_PRIORITY, CreateFileRequestAsync, ReportCategory.JAVASCRIPT, ct)).UnwrapAndRethrow(); + IDCLTypedArray content = (await intent.RepeatLoopAsync(NoAcquiredBudget.INSTANCE, PartitionComponent.TOP_PRIORITY, CreateFileRequestAsync, ReportCategory.JAVASCRIPT, ct)).UnwrapAndRethrow(); return new IRuntime.ReadFileResponse { diff --git a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/SDKObservableEventsEngineAPIImplementation.cs b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/SDKObservableEventsEngineAPIImplementation.cs index 3d30a36f3f8..2c9a8a390ce 100644 --- a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/SDKObservableEventsEngineAPIImplementation.cs +++ b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/SDKObservableEventsEngineAPIImplementation.cs @@ -28,10 +28,17 @@ public class SDKObservableEventsEngineAPIImplementation : EngineAPIImplementatio public SDKObservableEventsEngineAPIImplementation(ISharedPoolsProvider poolsProvider, IInstancePoolsProvider instancePoolsProvider, ICRDTProtocol crdtProtocol, ICRDTDeserializer crdtDeserializer, ICRDTSerializer crdtSerializer, ICRDTWorldSynchronizer crdtWorldSynchronizer, IOutgoingCRDTMessagesProvider outgoingCrtdMessagesProvider, - ISystemGroupsUpdateGate systemGroupsUpdateGate, ISceneExceptionsHandler exceptionsHandler, - MultiThreadSync multiThreadSync, MultiThreadSync.Owner syncOwner) : base(poolsProvider, instancePoolsProvider, crdtProtocol, + ISystemGroupsUpdateGate systemGroupsUpdateGate, ISceneExceptionsHandler exceptionsHandler +#if !UNITY_WEBGL + , MultiThreadSync multiThreadSync, MultiThreadSync.Owner syncOwner +#endif + ) : base(poolsProvider, instancePoolsProvider, crdtProtocol, crdtDeserializer, crdtSerializer, crdtWorldSynchronizer, outgoingCrtdMessagesProvider, - systemGroupsUpdateGate, exceptionsHandler, multiThreadSync, syncOwner) { } + systemGroupsUpdateGate, exceptionsHandler +#if !UNITY_WEBGL + , multiThreadSync, syncOwner +#endif + ) { } public void TryAddSubscription(string eventId) { diff --git a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs index 74441bbc29c..f207c9f27fe 100644 --- a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs +++ b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/CommunicationControllerAPIImplementationShould.cs @@ -6,7 +6,9 @@ using DCL.Multiplayer.Connections.Messaging.Pipe; using ECS; using Microsoft.ClearScript; +using Microsoft.ClearScript.JavaScript; using Microsoft.ClearScript.V8; +using SceneRuntime.V8; using NSubstitute; using NUnit.Framework; using SceneRunner.Scene; @@ -49,9 +51,9 @@ public void SetUp() var uint8ArrayCtor = (ScriptObject)engine.Global.GetProperty("Uint8Array"); jsOperations = Substitute.For(); - jsOperations.NewArray().Returns(_ => arrayCtor.Invoke(true)); + jsOperations.NewArray().Returns(_ => new V8ScriptObjectAdapter((ScriptObject)arrayCtor.Invoke(true))); - jsOperations.GetTempUint8Array().Returns(_ => uint8ArrayCtor.Invoke(true, IJsOperations.LIVEKIT_MAX_SIZE)); + jsOperations.GetTempUint8Array().Returns(_ => new V8TypedArrayAdapter((ITypedArray)uint8ArrayCtor.Invoke(true, IJsOperations.LIVEKIT_MAX_SIZE))); api = new CommunicationsControllerAPIImplementation( sceneData, diff --git a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/EngineAPIImplementationShould.cs b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/EngineAPIImplementationShould.cs index 616bef0e549..2ce0fdf946c 100644 --- a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/EngineAPIImplementationShould.cs +++ b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/JsModulesImplementation/Tests/EngineAPIImplementationShould.cs @@ -77,8 +77,13 @@ public void SetUp() crdtWorldSynchronizer = Substitute.For(), outgoingCrtdMessagesProvider = Substitute.For(), Substitute.For(), - new RethrowSceneExceptionsHandler(), - new MultiThreadSync(new SceneShortInfo()), new MultiThreadSync.Owner("TEST")); + new RethrowSceneExceptionsHandler() +#if !UNITY_WEBGL + , + new MultiThreadSync(new SceneShortInfo()), + new MultiThreadSync.Owner("TEST") +#endif + ); crdtDeserializer.When(d => d.DeserializeBatch(ref Arg.Any>(), Arg.Any>())) .Do(c => diff --git a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/PoolsProviders/InstancePoolsProviderExtensions.cs b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/PoolsProviders/InstancePoolsProviderExtensions.cs index 27f12e1366a..8b47b46f232 100644 --- a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/PoolsProviders/InstancePoolsProviderExtensions.cs +++ b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/PoolsProviders/InstancePoolsProviderExtensions.cs @@ -1,11 +1,11 @@ -using Microsoft.ClearScript.JavaScript; +using Utility; namespace CrdtEcsBridge.PoolsProviders { public static class InstancePoolsProviderExtensions { public static void RenewCrdtRawDataPoolFromScriptArray( - this IInstancePoolsProvider instancePoolsProvider, ITypedArray scriptArray, + this IInstancePoolsProvider instancePoolsProvider, IDCLTypedArray scriptArray, ref PoolableByteArray lastInput) { EnsureArrayLength(instancePoolsProvider, (int)scriptArray.Length, ref lastInput); diff --git a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/PoolsProviders/PoolableByteArray.cs b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/PoolsProviders/PoolableByteArray.cs index 7e7e1aa01f9..2a9287118c6 100644 --- a/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/PoolsProviders/PoolableByteArray.cs +++ b/Explorer/Assets/DCL/Infrastructure/CrdtEcsBridge/PoolsProviders/PoolableByteArray.cs @@ -1,4 +1,6 @@ -using Microsoft.ClearScript.V8.FastProxy; +#if !UNITY_WEBGL + +using Microsoft.ClearScript.V8.FastProxy; using System; namespace CrdtEcsBridge.PoolsProviders @@ -80,3 +82,116 @@ public bool MoveNext() => } } } + +#else + +// WebGL version + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace CrdtEcsBridge.PoolsProviders +{ + public struct PoolableByteArray : IDisposable, IEnumerable + { + public static readonly PoolableByteArray EMPTY = new (System.Array.Empty(), 0, null); + + public readonly byte[] Array; + public readonly Action ReleaseFunc; + + public PoolableByteArray(byte[] array, int length, Action releaseFunc) + { + Array = array; + Length = length; + ReleaseFunc = releaseFunc; + IsDisposed = false; + } + + public int Length { get; private set; } + + public Span Span => new(Array, 0, Length); + + public Memory Memory => Array.AsMemory(0, Length); + + public bool IsDisposed { get; private set; } + + public bool IsEmpty => Length == 0; + + public void SetLength(int length) + { + if (length > Array.Length) + throw new ArgumentOutOfRangeException(nameof(length), $"Rented Array Size {Array.Length} is lower than the requested {length}"); + + Length = length; + } + + public void Dispose() + { + if (IsEmpty || IsDisposed) return; + + ReleaseFunc(Array); + + IsDisposed = true; + } + + public Enumerator GetEnumerator() => + new Enumerator(this); + + IEnumerator IEnumerable.GetEnumerator() => + GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => + GetEnumerator(); + + public struct Enumerator : IEnumerator + { + private readonly byte[] _array; + private readonly int _end; // cache Offset + Count, since it's a little slow + private int _current; + + internal Enumerator(PoolableByteArray arraySegment) + { + _array = arraySegment.Array; + _end = arraySegment.Length; + _current = -1; + } + + public bool MoveNext() + { + if (_current < _end) + { + _current++; + return _current < _end; + } + + return false; + } + + public byte Current + { + get + { + if (_current < -1) + throw new InvalidOperationException("EnumNotStarted"); + + if (_current >= _end) + throw new InvalidOperationException("EnumEnded"); + + return _array[_current]; + } + } + + object IEnumerator.Current => Current; + + void IEnumerator.Reset() + { + _current = -1; + } + + public void Dispose() { } + } + } +} + +#endif diff --git a/Explorer/Assets/DCL/Infrastructure/ECS/LifeCycle/Systems/LockECSSystem.cs b/Explorer/Assets/DCL/Infrastructure/ECS/LifeCycle/Systems/LockECSSystem.cs index aeff44994e5..377aacf4e7a 100644 --- a/Explorer/Assets/DCL/Infrastructure/ECS/LifeCycle/Systems/LockECSSystem.cs +++ b/Explorer/Assets/DCL/Infrastructure/ECS/LifeCycle/Systems/LockECSSystem.cs @@ -7,6 +7,11 @@ namespace ECS.LifeCycle.Systems { + +// MultiThreadSync is not required in WebGL because it's always the single threaded environment +// Even if we use WebWorkers they still provide message passing to the main thread safely +#if !UNITY_WEBGL + /// /// Locks ECS from modification while the whole cycle of the Unity systems is running /// @@ -28,4 +33,6 @@ protected override void Update(float t) boxedScope.Acquire(owner); } } +#endif + } diff --git a/Explorer/Assets/DCL/Infrastructure/ECS/LifeCycle/Systems/UnlockECSSystem.cs b/Explorer/Assets/DCL/Infrastructure/ECS/LifeCycle/Systems/UnlockECSSystem.cs index 6a82449a3b7..3bf45abd95f 100644 --- a/Explorer/Assets/DCL/Infrastructure/ECS/LifeCycle/Systems/UnlockECSSystem.cs +++ b/Explorer/Assets/DCL/Infrastructure/ECS/LifeCycle/Systems/UnlockECSSystem.cs @@ -7,6 +7,10 @@ namespace ECS.LifeCycle.Systems { + +// MultiThreadSync is not required in WebGL because it's always the single threaded environment +// Even if we use WebWorkers they still provide message passing to the main thread safely +#if !UNITY_WEBGL /// /// Unlocks ECS when the whole cycle of the player loop has processed /// @@ -27,4 +31,6 @@ protected override void Update(float t) boxedScope.ReleaseIfAcquired(); } } +#endif + } diff --git a/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/AssetBundles/LoadAssetBundleSystem.cs b/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/AssetBundles/LoadAssetBundleSystem.cs index 676ed3277da..e753c10f17c 100644 --- a/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/AssetBundles/LoadAssetBundleSystem.cs +++ b/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/AssetBundles/LoadAssetBundleSystem.cs @@ -65,7 +65,7 @@ protected override async UniTask> FlowI AssetBundle? assetBundle = null; AssetBundleLoadingResult? assetBundleResult = null; -#if UNITY_WEBGL +#if UNITY_WEBGL && FORCE_SHADER_BUNDLE_PRELOADER if (ShaderBundlePreloader.TryGetPreloadedBundle(intention.Hash ?? "", out AssetBundle? preloaded)) assetBundle = preloaded; #endif diff --git a/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/Cache/Disk/Playgrounds/DiskCachePlayground.cs b/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/Cache/Disk/Playgrounds/DiskCachePlayground.cs index dcabf66149b..6a31790e6c7 100644 --- a/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/Cache/Disk/Playgrounds/DiskCachePlayground.cs +++ b/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/Cache/Disk/Playgrounds/DiskCachePlayground.cs @@ -1,9 +1,15 @@ +// TRUST_WEBGL_THREAD_SAFETY_FLAG +#if !UNITY_WEBGL + using Cysharp.Threading.Tasks; using DCL.Optimization.Hashing; using ECS.StreamableLoading.Cache.Disk.CleanUp; using ECS.StreamableLoading.Cache.Disk.Lock; using System; using System.IO; + +#endif + using UnityEngine; namespace ECS.StreamableLoading.Cache.Disk.Playgrounds @@ -15,11 +21,12 @@ public class DiskCachePlayground : MonoBehaviour [SerializeField] private bool useCleanUp = true; +#if !UNITY_WEBGL private void Start() { StartAsync().Forget(); } - + private void Update() { CleanUp(); @@ -67,5 +74,8 @@ public void CleanUp() var _ = NewDiskCache(out var cache); cache.CleanUpIfNeeded(); } + +#endif + } } diff --git a/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/Cache/Disk/Playgrounds/TexturesDiskCachePlayground.cs b/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/Cache/Disk/Playgrounds/TexturesDiskCachePlayground.cs index 4bfc7afbcc1..9abbe285b58 100644 --- a/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/Cache/Disk/Playgrounds/TexturesDiskCachePlayground.cs +++ b/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/Cache/Disk/Playgrounds/TexturesDiskCachePlayground.cs @@ -1,3 +1,6 @@ +// TRUST_WEBGL_THREAD_SAFETY_FLAG +#if !UNITY_WEBGL + using Cysharp.Threading.Tasks; using DCL.Optimization.Hashing; using ECS.StreamableLoading.Cache.Disk.CleanUp; @@ -6,6 +9,9 @@ using System; using System.IO; using Unity.Collections; + +#endif + using UnityEngine; namespace ECS.StreamableLoading.Cache.Disk.Playgrounds @@ -14,6 +20,8 @@ public class TexturesDiskCachePlayground : MonoBehaviour { [SerializeField] private string cacheDirectory = string.Empty; [SerializeField] private Texture2D texture = null!; + +#if !UNITY_WEBGL private const string TEST_FILE = "texture.png"; private void Start() @@ -56,5 +64,8 @@ public async UniTaskVoid RemoveAsync() var result = await diskCache.RemoveAsync(hashKey, Path.GetExtension(TEST_FILE), destroyCancellationToken); print($"Remove result: success {result.Success} and error {result.Error?.Message}"); } + +#endif + } } diff --git a/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/DownloadProvider/GltFastDownloadStrategy.cs b/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/DownloadProvider/GltFastDownloadStrategy.cs index a99032c97ab..ca970bd8a9f 100644 --- a/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/DownloadProvider/GltFastDownloadStrategy.cs +++ b/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/DownloadProvider/GltFastDownloadStrategy.cs @@ -1,3 +1,7 @@ +// GLTFast forces usage of Task that is not compatible with WebGL +// TRUST_WEBGL_SYSTEM_TASKS_SAFETY_FLAG +#if !UNITY_WEBGL + using Arch.Core; using DCL.Optimization.PerformanceBudgeting; using DCL.WebRequests; @@ -51,3 +55,5 @@ public IGLTFastDisposableDownloadProvider CreateDownloadProvider(World world, Ge new GltFastGlobalDownloadProvider(world, realmData.Ipfs.ContentBaseUrl.Value, partitionComponent, reportData, webRequestController, acquiredBudget); } } + +#endif diff --git a/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/DownloadProvider/GltFastGlobalDownloadProvider.cs b/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/DownloadProvider/GltFastGlobalDownloadProvider.cs index 48cca9c57d5..0579ee00839 100644 --- a/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/DownloadProvider/GltFastGlobalDownloadProvider.cs +++ b/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/DownloadProvider/GltFastGlobalDownloadProvider.cs @@ -1,3 +1,7 @@ +// GLTFast forces usage of Task that is not compatible with WebGL +// TRUST_WEBGL_SYSTEM_TASKS_SAFETY_FLAG +#if !UNITY_WEBGL + using Arch.Core; using Cysharp.Threading.Tasks; using DCL.Optimization.PerformanceBudgeting; @@ -78,3 +82,5 @@ protected override string GetTextureErrorMessage(Promise promiseResult) } } } + +#endif diff --git a/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/DownloadProvider/GltFastSceneDownloadProvider.cs b/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/DownloadProvider/GltFastSceneDownloadProvider.cs index 319428bc862..836e1afb3e2 100644 --- a/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/DownloadProvider/GltFastSceneDownloadProvider.cs +++ b/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/DownloadProvider/GltFastSceneDownloadProvider.cs @@ -1,3 +1,7 @@ +// GLTFast forces usage of Task that is not compatible with WebGL +// TRUST_WEBGL_SYSTEM_TASKS_SAFETY_FLAG +#if !UNITY_WEBGL + using Arch.Core; using CommunicationData.URLHelpers; using Cysharp.Threading.Tasks; @@ -61,3 +65,5 @@ private string GetFileOriginalPathFromUri(Uri uri) } } } + +#endif diff --git a/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/LoadGLTFSystem.cs b/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/LoadGLTFSystem.cs index 9bb48059565..ce94295b00d 100644 --- a/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/LoadGLTFSystem.cs +++ b/Explorer/Assets/DCL/Infrastructure/ECS/StreamableLoading/GLTF/LoadGLTFSystem.cs @@ -1,3 +1,6 @@ +// GLTF is not supported at the moment +#if !UNITY_WEBGL + using Arch.Core; using Arch.SystemGroups; using Cysharp.Threading.Tasks; @@ -263,3 +266,5 @@ private static void CaptureHierarchyPathsRecursive(Transform transform, string c } } } + +#endif diff --git a/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/Bootstraper.cs b/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/Bootstraper.cs index ffc259a1a31..89613e8cfd4 100644 --- a/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/Bootstraper.cs +++ b/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/Bootstraper.cs @@ -245,7 +245,14 @@ Entity playerEntity { var memoryCache = new MemoryCache(); staticContainer.CacheCleaner.Register(memoryCache); - webJsSources = new CachedWebJsSources(webJsSources, memoryCache, new DiskCache>(diskCache, new StringDiskSerializer())); + +#if UNITY_WEBGL + var diskCacheInstance = IDiskCache.Null.INSTANCE; +#else + var diskCacheInstance = new DiskCache>(diskCache, new StringDiskSerializer()); +#endif + + webJsSources = new CachedWebJsSources(webJsSources, memoryCache, diskCacheInstance); } SceneSharedContainer sceneSharedContainer = SceneSharedContainer.Create( diff --git a/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/DynamicWorldContainer.cs b/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/DynamicWorldContainer.cs index dcb13bee052..7fe35587118 100644 --- a/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/DynamicWorldContainer.cs +++ b/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/DynamicWorldContainer.cs @@ -690,8 +690,10 @@ await MapRendererContainer // Local scene development scenes are excluded from deeplink runtime handling logic if (appArgs.HasFlag(AppArgsFlags.LOCAL_SCENE) == false) { +#if !UNITY_WEBGL DeepLinkHandle deepLinkHandleImplementation = new DeepLinkHandle(dynamicWorldParams.StartParcel, chatTeleporter, ct, communitiesDataService); deepLinkHandleImplementation.StartListenForDeepLinksAsync(ct).Forget(); +#endif } var passportBridge = new MVCPassportBridge(mvcManager); @@ -1175,8 +1177,10 @@ await MapRendererContainer } +#if !UNITY_WEBGL if (FeaturesRegistry.Instance.IsEnabled(FeatureId.LOCAL_SCENE_DEVELOPMENT) || FeaturesRegistry.Instance.IsEnabled(FeatureId.SELF_PREVIEW_BUILDER_COLLECTIONS)) globalPlugins.Add(new GlobalGLTFLoadingPlugin(staticContainer.WebRequestsContainer.WebRequestController, staticContainer.RealmData, builderContentURL.Value, localSceneDevelopment, staticContainer.ComponentsContainer.ComponentPoolsRegistry.RootContainerTransform())); +#endif globalPlugins.AddRange(staticContainer.SharedPlugins); diff --git a/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/MainSceneLoader.cs b/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/MainSceneLoader.cs index 64f8eb6a585..73a759e9276 100644 --- a/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/MainSceneLoader.cs +++ b/Explorer/Assets/DCL/Infrastructure/Global/Dynamic/MainSceneLoader.cs @@ -621,8 +621,12 @@ private static IDiskCache NewInstancePartialDiskCache(IAppA else diskCleanUp = new LRUDiskCleanUp(cacheDirectory, filesLock); +#if UNITY_WEBGL + return IDiskCache.Null.INSTANCE; +#else var partialCache = new DiskCache>(new DiskCache(cacheDirectory, filesLock, diskCleanUp), new PartialDiskSerializer()); return partialCache; +#endif } private static IDiskCache NewInstanceDiskCache(IAppArgs appArgs, RealmLaunchSettings launchSettings) @@ -652,8 +656,12 @@ private static IDiskCache NewInstanceDiskCache(IAppArgs appArgs, RealmLaunchSett else diskCleanUp = new LRUDiskCleanUp(cacheDirectory, filesLock); +#if UNITY_WEBGL + return new IDiskCache.Fake(); +#else var diskCache = new DiskCache(cacheDirectory, filesLock, diskCleanUp); return diskCache; +#endif } [ContextMenu(nameof(ValidateSettingsAsync))] diff --git a/Explorer/Assets/DCL/Infrastructure/Global/StaticContainer.cs b/Explorer/Assets/DCL/Infrastructure/Global/StaticContainer.cs index 815b80c1af4..7c2f4c604ca 100644 --- a/Explorer/Assets/DCL/Infrastructure/Global/StaticContainer.cs +++ b/Explorer/Assets/DCL/Infrastructure/Global/StaticContainer.cs @@ -248,7 +248,11 @@ public UniTask InitializeAsync(StaticSettings settings, CancellationToken ct) var assetBundlePlugin = new AssetBundlesPlugin(reportHandlingSettings, container.CacheCleaner, container.WebRequestsContainer.WebRequestController, buffersPool, partialsDiskCache, URLDomain.FromString(decentralandUrlsSource.Url(DecentralandUrl.AssetBundlesCDN)), container.GltfContainerAssetsCache); +#if UNITY_WEBGL + var textureDiskCache = IDiskCache.Null.INSTANCE; +#else var textureDiskCache = new DiskCache>(diskCache, new TextureDiskSerializer()); +#endif var textureResolvePlugin = new TexturesLoadingPlugin(container.WebRequestsContainer.WebRequestController, container.CacheCleaner, textureDiskCache, launchMode, container.ProfilesContainer.Repository); diagnosticsContainer.AddSentryScopeConfigurator(scope => @@ -284,7 +288,9 @@ public UniTask InitializeAsync(StaticSettings settings, CancellationToken ct) container.ECSWorldPlugins = new IDCLWorldPlugin[] { +#if !UNITY_WEBGL new GltfContainerPlugin(sharedDependencies, container.CacheCleaner, container.SceneReadinessReportQueue, launchMode, useRemoteAssetBundles, container.WebRequestsContainer.WebRequestController, container.LoadingStatus, container.GltfContainerAssetsCache, appArgs, componentsContainer.ComponentPoolsRegistry.RootContainerTransform()), +#endif new TransformsPlugin(sharedDependencies, exposedPlayerTransform, exposedGlobalDataContainer.ExposedCameraData), new BillboardPlugin(exposedGlobalDataContainer.ExposedCameraData), new NFTShapePlugin(decentralandUrlsSource, container.assetsProvisioner, sharedDependencies.FrameTimeBudget, componentsContainer.ComponentPoolsRegistry, container.WebRequestsContainer.WebRequestController, container.CacheCleaner, container.MediaContainer.mediaFactoryBuilder), diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRunner/ECSWorld/ECSWorldFactory.cs b/Explorer/Assets/DCL/Infrastructure/SceneRunner/ECSWorld/ECSWorldFactory.cs index d2b28637335..889d5a4b74c 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRunner/ECSWorld/ECSWorldFactory.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRunner/ECSWorld/ECSWorldFactory.cs @@ -87,12 +87,14 @@ public ECSWorldFacade CreateWorld(in ECSWorldFactoryArgs args) finalizeWorldSystems.Add(ReleaseReferenceComponentsSystem.InjectToWorld(ref builder, componentPoolsRegistry)); finalizeWorldSystems.Add(ReleaseRemovedComponentsSystem.InjectToWorld(ref builder)); +#if !UNITY_WEBGL var scope = new MultiThreadSync.BoxedScope(sharedDependencies.MultiThreadSync); var mutexOwner = new MultiThreadSync.Owner("ECSLoopSystem"); // These system will prevent changes from the JS scenes to squeeze in between different stages of the PlayerLoop at the same frame LockECSSystem.InjectToWorld(ref builder, scope, mutexOwner); UnlockECSSystem.InjectToWorld(ref builder, scope); +#endif SystemGroupWorld systemsWorld = builder.Finish(singletonDependencies.AggregateFactory, scenePartition).EnsureNotNull(); diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRunner/Scene/SceneAdmins.cs b/Explorer/Assets/DCL/Infrastructure/SceneRunner/Scene/SceneAdmins.cs deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRunner/SceneFacade.cs b/Explorer/Assets/DCL/Infrastructure/SceneRunner/SceneFacade.cs index 4d040c5bc20..d30e4608677 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRunner/SceneFacade.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRunner/SceneFacade.cs @@ -5,6 +5,7 @@ using SceneRunner.Scene; using SceneRunner.Scene.ExceptionsHandling; using SceneRuntime; +using SceneRuntime.V8; using System; using System.Diagnostics; using System.Threading; @@ -50,7 +51,7 @@ public void Initialize() /// /// is a component in the global scene as an - /// . It owns its through its + /// . It owns its through its /// field, which in turns owns its . So that also /// shall be the chain of Dispose calls. /// @@ -111,7 +112,7 @@ public async UniTask StartUpdateLoopAsync(int targetFPS, CancellationToken ct) } finally { sceneCodeIsRunning.Reset(); } - MultithreadingUtility.AssertMainThread(nameof(SceneRuntimeImpl.StartScene)); + MultithreadingUtility.AssertMainThread(nameof(V8SceneRuntimeImpl.StartScene)); var stopWatch = new Stopwatch(); var deltaTime = 0f; @@ -140,7 +141,7 @@ public async UniTask StartUpdateLoopAsync(int targetFPS, CancellationToken ct) SceneStateProvider.TickNumber++; - MultithreadingUtility.AssertMainThread(nameof(SceneRuntimeImpl.UpdateScene)); + MultithreadingUtility.AssertMainThread(nameof(V8SceneRuntimeImpl.UpdateScene)); // Passing ct to Task.Delay allows to break the loop immediately // as, otherwise, due to 0 or low FPS it can spin for much longer @@ -201,7 +202,7 @@ public void SetIsCurrent(bool isCurrent) /// /// is a component in the global scene as an - /// . It owns its through its + /// . It owns its through its /// field, which in turns owns its . So that also /// shall be the chain of Dispose calls. /// diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRunner/SceneFactory.cs b/Explorer/Assets/DCL/Infrastructure/SceneRunner/SceneFactory.cs index 737efaecf07..519bfd93c9e 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRunner/SceneFactory.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRunner/SceneFactory.cs @@ -30,6 +30,7 @@ using SceneRuntime.Apis.Modules.EngineApi.SDKObservableEvents; using SceneRuntime.Factory; using SceneRuntime.ScenePermissions; +using SceneRuntime.V8; using System; using System.Threading; using UnityEngine; @@ -174,7 +175,7 @@ private async UniTask CreateSceneAsync(ISceneData sceneData, IJsAp var deps = new SceneInstanceDependencies(sdkComponentsRegistry, entityCollidersGlobalCache, sceneData, permissionsProvider, partitionProvider, ecsWorldFactory, entityFactory); // Try to create scene runtime - SceneRuntimeImpl sceneRuntime; + V8SceneRuntimeImpl sceneRuntime; try { sceneRuntime = await sceneRuntimeFactory.CreateByPathAsync(deps.SceneCodeUrl, deps.PoolsProvider, sceneData.SceneShortInfo, ct, SceneRuntimeFactory.InstantiationBehavior.SwitchToThreadPool); } catch (Exception e) @@ -193,7 +194,9 @@ private async UniTask CreateSceneAsync(ISceneData sceneData, IJsAp SceneInstanceDependencies.WithRuntimeAndJsAPIBase runtimeDeps; +#if !UNITY_WEBGL var engineAPIMutexOwner = new MultiThreadSync.Owner(nameof(EngineAPIImplementation)); +#endif var ethereumApiImpl = new RestrictedEthereumApi(ethereumApi, permissionsProvider); if (ENABLE_SDK_OBSERVABLES) @@ -203,7 +206,11 @@ private async UniTask CreateSceneAsync(ISceneData sceneData, IJsAp runtimeDeps = new SceneInstanceDependencies.WithRuntimeJsAndSDKObservablesEngineAPI(deps, sceneRuntime, sharedPoolsProvider, crdtSerializer, mvcManager, globalWorldActions, realmData, messagePipesHub, - webRequestController, skyboxSettings, engineAPIMutexOwner, profileRepository, systemClipboard, roomHub, installSource); + webRequestController, skyboxSettings, +#if !UNITY_WEBGL + engineAPIMutexOwner, +#endif + profileRepository, systemClipboard, roomHub, installSource); sceneRuntime.RegisterAll( (ISDKObservableEventsEngineApi)runtimeDeps.EngineAPI, @@ -233,7 +240,11 @@ private async UniTask CreateSceneAsync(ISceneData sceneData, IJsAp { runtimeDeps = new SceneInstanceDependencies.WithRuntimeAndJsAPI(deps, sceneRuntime, sharedPoolsProvider, crdtSerializer, mvcManager, globalWorldActions, realmData, messagePipesHub, webRequestController, - skyboxSettings, engineAPIMutexOwner, profileRepository, systemClipboard, roomHub, installSource); + skyboxSettings, +#if !UNITY_WEBGL + engineAPIMutexOwner, +#endif + profileRepository, systemClipboard, roomHub, installSource); sceneRuntime.RegisterAll( runtimeDeps.EngineAPI, diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRunner/SceneInstanceDeps.cs b/Explorer/Assets/DCL/Infrastructure/SceneRunner/SceneInstanceDeps.cs index 9348de22f74..f26e8e9f32d 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRunner/SceneInstanceDeps.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRunner/SceneInstanceDeps.cs @@ -42,6 +42,7 @@ using SceneRuntime.Apis.Modules.Runtime; using SceneRuntime.Apis.Modules.SceneApi; using SceneRuntime.ScenePermissions; +using SceneRuntime.V8; using System; using System.Collections.Generic; using Utility.Multithreading; @@ -72,7 +73,9 @@ public class SceneInstanceDependencies : IDisposable private readonly ISceneData sceneData; private readonly IJsApiPermissionsProvider permissionsProvider; +#if !UNITY_WEBGL private readonly MultiThreadSync ecsMultiThreadSync; +#endif private readonly ICRDTDeserializer crdtDeserializer; private readonly IECSToCRDTWriter ecsToCRDTWriter; @@ -94,7 +97,9 @@ internal SceneInstanceDependencies( URLAddress sceneCodeUrl, SceneEcsExecutor ecsExecutor, ISceneData sceneData, +#if !UNITY_WEBGL MultiThreadSync ecsMultiThreadSync, +#endif ICRDTDeserializer crdtDeserializer, IECSToCRDTWriter ecsToCRDTWriter, ISystemGroupsUpdateGate systemGroupThrottler, @@ -113,7 +118,9 @@ internal SceneInstanceDependencies( SceneCodeUrl = sceneCodeUrl; EcsExecutor = ecsExecutor; this.sceneData = sceneData; +#if !UNITY_WEBGL this.ecsMultiThreadSync = ecsMultiThreadSync; +#endif this.crdtDeserializer = crdtDeserializer; this.ecsToCRDTWriter = ecsToCRDTWriter; this.systemGroupThrottler = systemGroupThrottler; @@ -133,7 +140,9 @@ public SceneInstanceDependencies( { this.sceneData = sceneData; this.permissionsProvider = permissionsProvider; +#if !UNITY_WEBGL ecsMultiThreadSync = new MultiThreadSync(sceneData.SceneShortInfo); +#endif CRDTProtocol = new CRDTProtocol(); SceneStateProvider = new SceneStateProvider(); systemGroupThrottler = new SystemGroupsUpdateGate(); @@ -149,7 +158,10 @@ public SceneInstanceDependencies( /* Pass dependencies here if they are needed by the systems */ ecsWorldSharedDependencies = new ECSWorldInstanceSharedDependencies(sceneData, partitionProvider, ecsToCRDTWriter, entitiesMap, - ExceptionsHandler, EntityCollidersCache, entityCollidersGlobalCache, SceneStateProvider, entityEventsBuilder, ecsMultiThreadSync, + ExceptionsHandler, EntityCollidersCache, entityCollidersGlobalCache, SceneStateProvider, entityEventsBuilder, +#if !UNITY_WEBGL + ecsMultiThreadSync, +#endif systemGroupThrottler, systemsUpdateGate); ECSWorldFacade = ecsWorldFactory.CreateWorld(new ECSWorldFactoryArgs(ecsWorldSharedDependencies, systemGroupThrottler, sceneData)); @@ -177,7 +189,9 @@ public void Dispose() systemGroupThrottler.Dispose(); systemsUpdateGate.Dispose(); EntityCollidersCache.Dispose(); +#if !UNITY_WEBGL ecsMultiThreadSync.Dispose(); +#endif ExceptionsHandler.Dispose(); } @@ -267,11 +281,13 @@ public void Dispose() internal class WithRuntimeAndJsAPI : WithRuntimeAndJsAPIBase { public WithRuntimeAndJsAPI - (SceneInstanceDependencies syncDeps, SceneRuntimeImpl sceneRuntime, ISharedPoolsProvider sharedPoolsProvider, ICRDTSerializer crdtSerializer, IMVCManager mvcManager, + (SceneInstanceDependencies syncDeps, V8SceneRuntimeImpl sceneRuntime, ISharedPoolsProvider sharedPoolsProvider, ICRDTSerializer crdtSerializer, IMVCManager mvcManager, IGlobalWorldActions globalWorldActions, IRealmData realmData, ISceneCommunicationPipe messagePipesHub, IWebRequestController webRequestController, SkyboxSettingsAsset skyboxSettings, +#if !UNITY_WEBGL MultiThreadSync.Owner syncOwner, +#endif IProfileRepository profileRepository, ISystemClipboard systemClipboard, IRoomHub roomHub, @@ -285,18 +301,25 @@ public WithRuntimeAndJsAPI syncDeps.CRDTWorldSynchronizer, syncDeps.OutgoingCRDTMessagesProvider, syncDeps.systemGroupThrottler, - syncDeps.ExceptionsHandler, + syncDeps.ExceptionsHandler +#if !UNITY_WEBGL + , syncDeps.ecsMultiThreadSync, - syncOwner), + syncOwner +#endif + ), syncDeps, sceneRuntime, sceneRuntime, mvcManager, globalWorldActions, realmData, profileRepository, messagePipesHub, webRequestController, skyboxSettings, systemClipboard, roomHub, installSource) { } } internal class WithRuntimeJsAndSDKObservablesEngineAPI : WithRuntimeAndJsAPIBase { public WithRuntimeJsAndSDKObservablesEngineAPI - (SceneInstanceDependencies syncDeps, SceneRuntimeImpl sceneRuntime, ISharedPoolsProvider sharedPoolsProvider, ICRDTSerializer crdtSerializer, IMVCManager mvcManager, + (SceneInstanceDependencies syncDeps, V8SceneRuntimeImpl sceneRuntime, ISharedPoolsProvider sharedPoolsProvider, ICRDTSerializer crdtSerializer, IMVCManager mvcManager, IGlobalWorldActions globalWorldActions, IRealmData realmData, ISceneCommunicationPipe messagePipesHub, - IWebRequestController webRequestController, SkyboxSettingsAsset skyboxSettings, MultiThreadSync.Owner syncOwner, + IWebRequestController webRequestController, SkyboxSettingsAsset skyboxSettings, +#if !UNITY_WEBGL + MultiThreadSync.Owner syncOwner, +#endif IProfileRepository profileRepository, ISystemClipboard systemClipboard, IRoomHub roomHub, @@ -311,9 +334,12 @@ public WithRuntimeJsAndSDKObservablesEngineAPI syncDeps.CRDTWorldSynchronizer, syncDeps.OutgoingCRDTMessagesProvider, syncDeps.systemGroupThrottler, - syncDeps.ExceptionsHandler, + syncDeps.ExceptionsHandler +#if !UNITY_WEBGL + , syncDeps.ecsMultiThreadSync, syncOwner +#endif ), syncDeps, sceneRuntime, diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRunner/Tests/SceneFacadeShould.cs b/Explorer/Assets/DCL/Infrastructure/SceneRunner/Tests/SceneFacadeShould.cs index c4397ef7698..4e16f98e8c8 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRunner/Tests/SceneFacadeShould.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRunner/Tests/SceneFacadeShould.cs @@ -387,7 +387,9 @@ public TestDeps(IECSWorldFactory worldFactory) : base( new URLAddress(), new SceneEcsExecutor(), Substitute.For(), +#if !UNITY_WEBGL new MultiThreadSync(new SceneShortInfo()), +#endif Substitute.For(), Substitute.For(), Substitute.For(), diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/CommunicationsControllerApi/CommunicationsControllerAPIWrapper.cs b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/CommunicationsControllerApi/CommunicationsControllerAPIWrapper.cs index 1b1fb7c41b1..6f01dfda8de 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/CommunicationsControllerApi/CommunicationsControllerAPIWrapper.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/CommunicationsControllerApi/CommunicationsControllerAPIWrapper.cs @@ -3,6 +3,7 @@ using Microsoft.ClearScript; using Microsoft.ClearScript.JavaScript; using SceneRunner.Scene.ExceptionsHandling; +using SceneRuntime.V8; using System; using System.Collections.Generic; using System.Threading; @@ -41,7 +42,7 @@ private void SendBinaryToParticipants(IList dataList, string? recipient) { for (var i = 0; i < dataList.Count; i++) { - var message = (ITypedArray)dataList[i]; + var message = new V8TypedArrayAdapter((ITypedArray)dataList[i]); PoolableByteArray element = PoolableByteArray.EMPTY; if (lastInput.Count <= i) diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/EngineApi/EngineApiWrapper.cs b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/EngineApi/EngineApiWrapper.cs index f6fc2ef4544..d40f485fb62 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/EngineApi/EngineApiWrapper.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/EngineApi/EngineApiWrapper.cs @@ -4,6 +4,7 @@ using SceneRunner.Scene; using SceneRunner.Scene.ExceptionsHandling; using SceneRuntime.Apis.Modules.EngineApi.SDKObservableEvents; +using SceneRuntime.V8; using System; using System.Threading; using UnityEngine.Profiling; @@ -41,7 +42,7 @@ public PoolableByteArray CrdtSendToRenderer(ITypedArray data) { Profiler.BeginThreadProfiling("SceneRuntime", threadName); - instancePoolsProvider.RenewCrdtRawDataPoolFromScriptArray(data, ref lastInput); + instancePoolsProvider.RenewCrdtRawDataPoolFromScriptArray(new V8TypedArrayAdapter(data), ref lastInput); PoolableByteArray result = api.CrdtSendToRenderer(lastInput.Memory); diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/Runtime/IRuntime.cs b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/Runtime/IRuntime.cs index 1ea1b707cbb..0c8dc2d3c99 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/Runtime/IRuntime.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/Runtime/IRuntime.cs @@ -1,11 +1,11 @@ using Cysharp.Threading.Tasks; using DCL.Ipfs; using ECS; -using Microsoft.ClearScript.JavaScript; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Threading; +using Utility; namespace SceneRuntime.Apis.Modules.Runtime { @@ -47,7 +47,7 @@ public struct GetWorldTimeResponse [Serializable] public struct ReadFileResponse { - public ITypedArray content; + public IDCLTypedArray content; public string hash; } diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/WebSocket/IWebSocketApi.cs b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/WebSocket/IWebSocketApi.cs index c357f91093d..477e6237606 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/WebSocket/IWebSocketApi.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Apis/Modules/WebSocket/IWebSocketApi.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Threading; +using Utility; namespace SceneRuntime.Apis.Modules { @@ -44,7 +45,7 @@ public struct ReceiveResponse /// Either "Binary" or "Text" /// public string type; - public ITypedArray binary; + public IDCLTypedArray binary; public string text; } } diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Factory/SceneRuntimeFactory.cs b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Factory/SceneRuntimeFactory.cs index 690082ce6a8..1ffd907f98d 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Factory/SceneRuntimeFactory.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Factory/SceneRuntimeFactory.cs @@ -7,6 +7,7 @@ using SceneRuntime.Factory.JsSceneSourceCode; using SceneRuntime.Factory.WebSceneSource; using SceneRuntime.Factory.WebSceneSource.Cache; +using SceneRuntime.V8; using System.Collections.Generic; using System.IO; using System.Linq; @@ -69,7 +70,7 @@ private static IJsSourcesCache EnabledJsScenesFileCachingOrIgnore() /// /// Must be called on the main thread /// - internal async UniTask CreateBySourceCodeAsync( + internal async UniTask CreateBySourceCodeAsync( string sourceCode, IInstancePoolsProvider instancePoolsProvider, SceneShortInfo sceneShortInfo, @@ -96,14 +97,14 @@ internal async UniTask CreateBySourceCodeAsync( string wrappedSource = WrapInModuleCommonJs(jsSceneLocalSourceCode.CodeForScene(sceneShortInfo.BaseParcel) ?? sourceCode); - return new SceneRuntimeImpl(wrappedSource, pair, moduleDictionary, sceneShortInfo, + return new V8SceneRuntimeImpl(wrappedSource, pair, moduleDictionary, sceneShortInfo, engineFactory); } /// /// Must be called on the main thread /// - public async UniTask CreateByPathAsync( + public async UniTask CreateByPathAsync( URLAddress path, IInstancePoolsProvider instancePoolsProvider, SceneShortInfo sceneShortInfo, diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Factory/Tests/JsCodeDiskSerializerShould.cs b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Factory/Tests/JsCodeDiskSerializerShould.cs index e0a750cb056..1b0f4374736 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Factory/Tests/JsCodeDiskSerializerShould.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Factory/Tests/JsCodeDiskSerializerShould.cs @@ -1,3 +1,5 @@ +#if !UNITY_WEBGL + using DCL.Optimization.Hashing; using ECS.StreamableLoading.Cache.Disk; using ECS.StreamableLoading.Cache.Disk.CleanUp; @@ -96,3 +98,5 @@ public async Task Cache() } } } + +#endif diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Factory/Tests/SceneRuntimeFactoryShould.cs b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Factory/Tests/SceneRuntimeFactoryShould.cs index 099f2a88736..033f31b7949 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Factory/Tests/SceneRuntimeFactoryShould.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Factory/Tests/SceneRuntimeFactoryShould.cs @@ -11,6 +11,7 @@ using SceneRunner.Scene.ExceptionsHandling; using SceneRuntime.Apis.Modules.EngineApi; using SceneRuntime.Factory.WebSceneSource; +using SceneRuntime.V8; using System.Collections; using System.Threading; using UnityEngine; @@ -55,7 +56,7 @@ await engineApi.crdtSendToRenderer({ data }) IInstancePoolsProvider instancePoolsProvider = Substitute.For(); instancePoolsProvider.GetAPIRawDataPool(Arg.Any()).Returns(c => new PoolableByteArray(new byte[c.Arg()], c.Arg(), null)); - using SceneRuntimeImpl sceneRuntime = await factory.CreateBySourceCodeAsync(sourceCode, + using V8SceneRuntimeImpl sceneRuntime = await factory.CreateBySourceCodeAsync(sourceCode, instancePoolsProvider, new SceneShortInfo(), CancellationToken.None); sceneRuntime.ExecuteSceneJson(); @@ -84,7 +85,7 @@ public IEnumerator CreateByPath() => instancePoolsProvider.GetAPIRawDataPool(Arg.Any()) .Returns(c => new PoolableByteArray(new byte[c.Arg()], c.Arg(), _ => { })); - using SceneRuntimeImpl sceneRuntime = await factory.CreateByPathAsync(path, + using V8SceneRuntimeImpl sceneRuntime = await factory.CreateByPathAsync(path, instancePoolsProvider, new SceneShortInfo(), CancellationToken.None); sceneRuntime.RegisterEngineAPI(Substitute.For(), engineApi, instancePoolsProvider, sceneExceptionsHandler); diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/IDCLScriptObject.cs b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/IDCLScriptObject.cs new file mode 100644 index 00000000000..abfcdeecfb4 --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/IDCLScriptObject.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; + +namespace SceneRuntime +{ + public interface IDCLScriptObject + { + /// + /// Enumerates the script object's property names. + /// + IEnumerable PropertyNames { get; } + + /// + /// Gets or sets the value of a named script object property. + /// + /// The name of the property to get or set. + /// Optional arguments for property access. + /// The value of the specified property. + object this[string name, params object[] args] { get; set; } + + /// + /// Gets the value of a named script object property. + /// + /// The name of the property to get. + /// Optional arguments for property retrieval. + /// The value of the specified property. + object GetProperty(string name, params object[] args); + + /// + /// Sets the value of a named script object property. + /// + /// The name of the property to set. + /// An array containing optional arguments and the new property value. + /// + /// The array must contain at least one element. The new + /// property value must be the last element of the array. + /// + void SetProperty(string name, params object[] args); + + /// + /// Sets the value of an indexed script object property. + /// + /// The index of the property to set. + /// The new property value. + void SetProperty(int index, object value); + + /// + /// Invokes the script object. + /// + /// True to invoke the object as a constructor, false otherwise. + /// Optional arguments for object invocation. + /// The invocation result value. + object Invoke(bool asConstructor, params object[] args); + + /// + /// Invokes a script object method. + /// + /// The name of the method to invoke. + /// Optional arguments for method invocation. + /// The invocation result value. + object InvokeMethod(string name, params object[] args); + + /// + /// Invokes the script object as a function. + /// + /// Optional arguments for object invocation. + /// The invocation result value. + object InvokeAsFunction(params object[] args); + + /// + /// Gets the native JavaScript object representation. + /// This allows adapters to unwrap themselves to return the platform-specific native object + /// that JavaScript can properly enumerate and interact with. + /// + /// The native JavaScript object (e.g., ScriptObject for V8, WebGLScriptObject for WebGL). + object GetNativeObject(); + } +} diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/IDCLScriptObject.cs.meta b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/IDCLScriptObject.cs.meta new file mode 100644 index 00000000000..ed9755b6146 --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/IDCLScriptObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 48f834f50d0949eda3dc7e48edd46575 +timeCreated: 1768239471 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/IJSArrayOperations.cs b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/IJSArrayOperations.cs index fcd708bf5e0..2e0c7f72aba 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/IJSArrayOperations.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/IJSArrayOperations.cs @@ -1,20 +1,20 @@ -using Microsoft.ClearScript; -using Microsoft.ClearScript.JavaScript; using System; +using Utility; namespace SceneRuntime { /// - /// Operations bound to the instance of . If it is disposed of, calling methods will throw + /// Operations bound to the instance of . If it is disposed of, calling methods will throw /// public interface IJsOperations : IDisposable { public const int LIVEKIT_MAX_SIZE = 1024 * 13; - ITypedArray GetTempUint8Array(); + IDCLTypedArray GetTempUint8Array(); - ScriptObject NewArray(); + IDCLScriptObject NewArray(); - ITypedArray NewUint8Array(int length); + IDCLTypedArray NewUint8Array(int length); } } + diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/SceneRuntime.asmdef b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/SceneRuntime.asmdef index 4182dc4941f..94a6868f252 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/SceneRuntime.asmdef +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/SceneRuntime.asmdef @@ -32,7 +32,7 @@ ], "includePlatforms": [], "excludePlatforms": [], - "allowUnsafeCode": false, + "allowUnsafeCode": true, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Tests/SceneRuntimeShould.cs b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Tests/SceneRuntimeShould.cs index 67f482f9240..fd5432704ba 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Tests/SceneRuntimeShould.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/Tests/SceneRuntimeShould.cs @@ -13,6 +13,7 @@ using SceneRuntime.Apis.Modules.EngineApi; using SceneRuntime.Factory; using SceneRuntime.Factory.WebSceneSource; +using SceneRuntime.V8; using System; using System.Collections; using System.Threading; @@ -56,7 +57,7 @@ public IEnumerator EngineApi_GetState() => "; var sceneRuntimeFactory = NewSceneRuntimeFactory(); - SceneRuntimeImpl sceneRuntime = await sceneRuntimeFactory.CreateBySourceCodeAsync(code, poolsProvider, new SceneShortInfo(), CancellationToken.None); + V8SceneRuntimeImpl sceneRuntime = await sceneRuntimeFactory.CreateBySourceCodeAsync(code, poolsProvider, new SceneShortInfo(), CancellationToken.None); sceneRuntime.RegisterEngineAPI(Substitute.For(), engineApi, Substitute.For(), sceneExceptionsHandler); sceneRuntime.ExecuteSceneJson(); @@ -83,7 +84,7 @@ await engineApi.crdtSendToRenderer({ data }) "; var sceneRuntimeFactory = NewSceneRuntimeFactory(); - SceneRuntimeImpl sceneRuntime = await sceneRuntimeFactory.CreateBySourceCodeAsync(code, poolsProvider, new SceneShortInfo(), CancellationToken.None); + V8SceneRuntimeImpl sceneRuntime = await sceneRuntimeFactory.CreateBySourceCodeAsync(code, poolsProvider, new SceneShortInfo(), CancellationToken.None); sceneRuntime.RegisterEngineAPI(Substitute.For(), engineApi, poolsProvider, sceneExceptionsHandler); sceneRuntime.ExecuteSceneJson(); @@ -121,7 +122,7 @@ public IEnumerator ProfileOnUpdate() => "; var sceneRuntimeFactory = NewSceneRuntimeFactory(); - SceneRuntimeImpl sceneRuntime = await sceneRuntimeFactory.CreateBySourceCodeAsync(code, poolsProvider, new SceneShortInfo(), CancellationToken.None); + V8SceneRuntimeImpl sceneRuntime = await sceneRuntimeFactory.CreateBySourceCodeAsync(code, poolsProvider, new SceneShortInfo(), CancellationToken.None); sceneRuntime.ExecuteSceneJson(); await sceneRuntime.StartScene(); @@ -148,7 +149,7 @@ public IEnumerator EngineApi_TestRealScene() => var sceneRuntimeFactory = NewSceneRuntimeFactory(); var path = URLAddress.FromString($"file://{Application.dataPath + "/../TestResources/Scenes/Cube/cube.js"}"); - SceneRuntimeImpl sceneRuntime = await sceneRuntimeFactory.CreateByPathAsync(path, poolsProvider, new SceneShortInfo(), CancellationToken.None); + V8SceneRuntimeImpl sceneRuntime = await sceneRuntimeFactory.CreateByPathAsync(path, poolsProvider, new SceneShortInfo(), CancellationToken.None); sceneRuntime.RegisterEngineAPI(Substitute.For(), engineApi, poolsProvider, sceneExceptionsHandler); sceneRuntime.ExecuteSceneJson(); diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8.meta b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8.meta new file mode 100644 index 00000000000..5f69c3ee584 --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d2d56d0d53109bb4a89743ff6beab263 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8ArrayBufferAdapter.cs b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8ArrayBufferAdapter.cs new file mode 100644 index 00000000000..3779d0ead2a --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8ArrayBufferAdapter.cs @@ -0,0 +1,32 @@ +using Microsoft.ClearScript.JavaScript; +using System; +using Utility; + +namespace SceneRuntime.V8 +{ + public class V8ArrayBufferAdapter : IDCLArrayBuffer + { + private readonly IArrayBuffer arrayBuffer; + + public V8ArrayBufferAdapter(IArrayBuffer arrayBuffer) + { + this.arrayBuffer = arrayBuffer; + } + + public IArrayBuffer ArrayBuffer => arrayBuffer; + + ulong IDCLArrayBuffer.Size => arrayBuffer.Size; + + ulong IDCLArrayBuffer.ReadBytes(ulong offset, ulong count, byte[] destination, ulong destinationIndex) => + arrayBuffer.ReadBytes(offset, count, destination, destinationIndex); + + ulong IDCLArrayBuffer.WriteBytes(byte[] source, ulong sourceIndex, ulong count, ulong offset) => + arrayBuffer.WriteBytes(source, sourceIndex, count, offset); + + void IDCLArrayBuffer.InvokeWithDirectAccess(Action action) => + arrayBuffer.InvokeWithDirectAccess(action); + + TResult IDCLArrayBuffer.InvokeWithDirectAccess(Func func) => + arrayBuffer.InvokeWithDirectAccess(func); + } +} diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8ArrayBufferAdapter.cs.meta b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8ArrayBufferAdapter.cs.meta new file mode 100644 index 00000000000..f12892d8162 --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8ArrayBufferAdapter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 0933338823357304ebda3c1f9c48f97f \ No newline at end of file diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/SceneRuntimeImpl.cs b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8SceneRuntimeImpl.cs similarity index 87% rename from Explorer/Assets/DCL/Infrastructure/SceneRuntime/SceneRuntimeImpl.cs rename to Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8SceneRuntimeImpl.cs index 78944a89174..1621217a1f0 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/SceneRuntimeImpl.cs +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8SceneRuntimeImpl.cs @@ -13,16 +13,16 @@ using System.Collections.Generic; using System.Threading; using UnityEngine.Assertions; +using Utility; -namespace SceneRuntime +namespace SceneRuntime.V8 { - // Avoid the same name for Namespace and Class - public sealed class SceneRuntimeImpl : ISceneRuntime, IJsOperations + public sealed class V8SceneRuntimeImpl : ISceneRuntime, IJsOperations { internal readonly V8ScriptEngine engine; private readonly ScriptObject arrayCtor; private readonly ScriptObject unit8ArrayCtor; - private readonly List> uint8Arrays; + private readonly List> uint8Arrays; private readonly JsApiBunch jsApiBunch; // ResetableSource is an optimization to reduce 11kb of memory allocation per Update (reduces 15kb to 4kb per update) @@ -39,7 +39,7 @@ public sealed class SceneRuntimeImpl : ISceneRuntime, IJsOperations CancellationTokenSource ISceneRuntime.isDisposingTokenSource => isDisposingTokenSource; - public SceneRuntimeImpl( + public V8SceneRuntimeImpl( string sourceCode, string initCode, IReadOnlyDictionary jsModules, @@ -75,13 +75,13 @@ V8EngineFactory engineFactory arrayCtor = (ScriptObject)engine.Global.GetProperty("Array"); unit8ArrayCtor = (ScriptObject)engine.Global.GetProperty("Uint8Array"); - uint8Arrays = new List>(); + uint8Arrays = new List>(); nextUint8Array = 0; } /// /// is a component in the global scene as an - /// . It owns its through its + /// . It owns its through its /// field, which in turns owns its . So that also /// shall be the chain of Dispose calls. /// @@ -162,17 +162,17 @@ public void ApplyStaticMessages(ReadOnlyMemory data) Assert.IsTrue(result.IsEmpty); } - ScriptObject IJsOperations.NewArray() => - (ScriptObject)arrayCtor.Invoke(true); + IDCLScriptObject IJsOperations.NewArray() => + new V8ScriptObjectAdapter((ScriptObject)arrayCtor.Invoke(true)); - ITypedArray IJsOperations.NewUint8Array(int length) => - (ITypedArray)unit8ArrayCtor.Invoke(true, length); + IDCLTypedArray IJsOperations.NewUint8Array(int length) => + new V8TypedArrayAdapter((ITypedArray)unit8ArrayCtor.Invoke(true, length)); - ITypedArray IJsOperations.GetTempUint8Array() + IDCLTypedArray IJsOperations.GetTempUint8Array() { if (nextUint8Array >= uint8Arrays.Count) - uint8Arrays.Add((ITypedArray)unit8ArrayCtor.Invoke(true, - IJsOperations.LIVEKIT_MAX_SIZE)); + uint8Arrays.Add(new V8TypedArrayAdapter((ITypedArray)unit8ArrayCtor.Invoke(true, + IJsOperations.LIVEKIT_MAX_SIZE))); return uint8Arrays[nextUint8Array++]; } diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/SceneRuntimeImpl.cs.meta b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8SceneRuntimeImpl.cs.meta similarity index 76% rename from Explorer/Assets/DCL/Infrastructure/SceneRuntime/SceneRuntimeImpl.cs.meta rename to Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8SceneRuntimeImpl.cs.meta index 751d218c3dd..dd6c1499151 100644 --- a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/SceneRuntimeImpl.cs.meta +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8SceneRuntimeImpl.cs.meta @@ -6,6 +6,6 @@ MonoImporter: defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8ScriptObjectAdapter.cs b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8ScriptObjectAdapter.cs new file mode 100644 index 00000000000..f3685d83505 --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8ScriptObjectAdapter.cs @@ -0,0 +1,66 @@ +using Microsoft.ClearScript; +using System.Collections.Generic; + +namespace SceneRuntime.V8 +{ + public class V8ScriptObjectAdapter : IDCLScriptObject + { + public ScriptObject ScriptObject { get; } + + public IEnumerable PropertyNames => ScriptObject.PropertyNames; + + public object this[string name, params object[] args] + { + get => ScriptObject[name, args]; + set => ScriptObject[name, args] = value; + } + + public V8ScriptObjectAdapter(ScriptObject scriptObject) + { + ScriptObject = scriptObject; + } + + /// + public object InvokeMethod(string name, params object[] args) => + ScriptObject.InvokeMethod(name, args); + + /// + public object InvokeAsFunction(params object[] args) => + ScriptObject.InvokeAsFunction(args); + + /// + public object GetProperty(string name, params object[] args) => + ScriptObject.GetProperty(name, args); + + /// + public void SetProperty(string name, params object[] args) => + ScriptObject.SetProperty(name, args); + + //TODO FRAN: Check this logic + /// + public void SetProperty(int index, object value) + { + // Convert adapters to ScriptObject so they're enumerable in JavaScript + // ClearScript can handle ScriptObject natively but not adapter wrappers + object valueToSet = value; + + if (value is V8ScriptObjectAdapter v8Adapter) + valueToSet = v8Adapter.ScriptObject; + else if (value is V8TypedArrayAdapter v8TypedAdapter) + valueToSet = v8TypedAdapter.ScriptObject; + + ScriptObject.SetProperty(index, valueToSet); + } + + /// + public object Invoke(bool asConstructor, params object[] args) => + ScriptObject.Invoke(asConstructor, args); + + /// + public object GetNativeObject() => + ScriptObject; + + public static implicit operator ScriptObject(V8ScriptObjectAdapter adapter) => + adapter.ScriptObject; + } +} diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8ScriptObjectAdapter.cs.meta b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8ScriptObjectAdapter.cs.meta new file mode 100644 index 00000000000..b1d98b04ce4 --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8ScriptObjectAdapter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2330bf97d34281d44811899adffbbc5f \ No newline at end of file diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8TypedArrayAdapter.cs b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8TypedArrayAdapter.cs new file mode 100644 index 00000000000..fa6170b3a31 --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8TypedArrayAdapter.cs @@ -0,0 +1,124 @@ +using Microsoft.ClearScript; +using Microsoft.ClearScript.JavaScript; +using System; +using System.Collections.Generic; +using Utility; + +namespace SceneRuntime.V8 +{ + public class V8TypedArrayAdapter : IDCLTypedArray, IDCLScriptObject + { + public ITypedArray TypedArray { get; } + + public ScriptObject ScriptObject { get; } + + ulong IDCLTypedArray.Length => TypedArray.Length; + + ulong IDCLTypedArray.Size => TypedArray.Size; + + IDCLArrayBuffer IDCLTypedArray.ArrayBuffer + { + get + { + IArrayBuffer arrayBuffer = TypedArray.ArrayBuffer; + return new V8ArrayBufferAdapter(arrayBuffer); + } + } + + IEnumerable IDCLScriptObject.PropertyNames => ScriptObject.PropertyNames; + + object IDCLScriptObject.this[string name, params object[] args] + { + get => ScriptObject[name, args]; + set => ScriptObject[name, args] = value; + } + + public V8TypedArrayAdapter(ITypedArray typedArray) + { + TypedArray = typedArray; + ScriptObject = (ScriptObject)typedArray; + } + + public static implicit operator ScriptObject(V8TypedArrayAdapter adapter) => + adapter.ScriptObject; + + ulong IDCLTypedArray.Read(ulong index, ulong length, byte[] destination, ulong destinationIndex) => + TypedArray.Read(index, length, destination, destinationIndex); + + void IDCLTypedArray.InvokeWithDirectAccess(Action action) => + TypedArray.InvokeWithDirectAccess(action); + + int IDCLTypedArray.InvokeWithDirectAccess(Func func) => + TypedArray.InvokeWithDirectAccess(func); + + void IDCLTypedArray.ReadBytes(ulong offset, ulong count, byte[] destination, ulong destinationIndex) => + TypedArray.Read(offset, count, destination, destinationIndex); + + void IDCLTypedArray.WriteBytes(byte[] source, ulong sourceIndex, ulong count, ulong offset) + { + if (count == 0) + return; + + TypedArray.InvokeWithDirectAccess(pData => + { + unsafe + { + byte* destPtr = (byte*)pData.ToPointer() + offset; + + fixed (byte* srcPtr = source) + { + byte* src = srcPtr + sourceIndex; + Buffer.MemoryCopy(src, destPtr, count, count); + } + } + }); + } + + object IDCLScriptObject.GetProperty(string name, params object[] args) => + ScriptObject.GetProperty(name, args); + + void IDCLScriptObject.SetProperty(string name, params object[] args) => + ScriptObject.SetProperty(name, args); + + void IDCLScriptObject.SetProperty(int index, object value) => + ScriptObject.SetProperty(index, value); + + //TODO FRAN: Check this logic + object IDCLScriptObject.Invoke(bool asConstructor, params object[] args) + { + object result = ScriptObject.Invoke(asConstructor, args); + + if (result is ITypedArray ta) + return new V8TypedArrayAdapter(ta); + + if (result is ScriptObject so) + return new V8ScriptObjectAdapter(so); + + return result; + } + + //TODO FRAN: Check this logic + object IDCLScriptObject.InvokeMethod(string name, params object[] args) + { + object result = ScriptObject.InvokeMethod(name, args); + + if (result is ITypedArray ta) { return new V8TypedArrayAdapter(ta); } + + if (result is not ScriptObject so) return result; + + try + { + var typedArray = (ITypedArray)so; + return new V8TypedArrayAdapter(typedArray); + } + catch { return new V8ScriptObjectAdapter(so); } + } + + object IDCLScriptObject.InvokeAsFunction(params object[] args) => + ScriptObject.InvokeAsFunction(args); + + /// + object IDCLScriptObject.GetNativeObject() => + ScriptObject; + } +} diff --git a/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8TypedArrayAdapter.cs.meta b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8TypedArrayAdapter.cs.meta new file mode 100644 index 00000000000..a28fce974d8 --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/SceneRuntime/V8/V8TypedArrayAdapter.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6781a174ec0dccc45ba30c18f0f34786 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Infrastructure/Utility/ClearScriptUtils.cs b/Explorer/Assets/DCL/Infrastructure/Utility/ClearScriptUtils.cs deleted file mode 100644 index 3c2527bf9d3..00000000000 --- a/Explorer/Assets/DCL/Infrastructure/Utility/ClearScriptUtils.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Microsoft.ClearScript.JavaScript; -using System; -using Unity.Collections; - -namespace Utility -{ - public static class ClearScriptUtils - { - public static int Write(this ITypedArray clearScriptArray, - NativeArray.ReadOnly source, ulong length, ulong index) where T : unmanaged - { - ulong totalLength = clearScriptArray.Length; - - if (index >= totalLength) { throw new ArgumentOutOfRangeException(nameof(index)); } - - return clearScriptArray.InvokeWithDirectAccess(pData => WriteByteArrayToUnmanagedMemory(source.AsReadOnlySpan(), (int)Math.Min(length, totalLength - index), clearScriptArray.GetPtrWithIndex(pData, index))); - } - - public static int Write(this ITypedArray clearScriptArray, ReadOnlyMemory source, - ulong length, ulong index) where T : unmanaged - { - ulong totalLength = clearScriptArray.Length; - - if (index >= totalLength) { throw new ArgumentOutOfRangeException(nameof(index)); } - - return clearScriptArray.InvokeWithDirectAccess(pData => WriteByteArrayToUnmanagedMemory(source.Span, (int)Math.Min(length, totalLength - index), clearScriptArray.GetPtrWithIndex(pData, index))); - } - - private static IntPtr GetPtrWithIndex(this ITypedArray clearScriptArray, IntPtr pData, - ulong index) where T : unmanaged - { - var baseAddr = unchecked((ulong)pData.ToInt64()); - return new IntPtr(unchecked((long)checked(baseAddr + (index * (clearScriptArray.Size / clearScriptArray.Length))))); - } - - private static unsafe int WriteByteArrayToUnmanagedMemory(ReadOnlySpan sourceArray, int length, IntPtr pDestination) where T: unmanaged - { - int sourceLength = sourceArray.Length; - length = Math.Min(length, sourceLength); - - var targetSpan = new Span(pDestination.ToPointer(), length); - sourceArray.CopyTo(targetSpan); - - return length; - } - } -} diff --git a/Explorer/Assets/DCL/Infrastructure/Utility/ClearScriptUtils.cs.meta b/Explorer/Assets/DCL/Infrastructure/Utility/ClearScriptUtils.cs.meta deleted file mode 100644 index e6671cd9830..00000000000 --- a/Explorer/Assets/DCL/Infrastructure/Utility/ClearScriptUtils.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 363a498ee5586824dba5224c08527184 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Explorer/Assets/DCL/Infrastructure/Utility/IDCLArrayBuffer.cs b/Explorer/Assets/DCL/Infrastructure/Utility/IDCLArrayBuffer.cs new file mode 100644 index 00000000000..4608c3062ea --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/Utility/IDCLArrayBuffer.cs @@ -0,0 +1,57 @@ +using System; + +namespace Utility +{ + public interface IDCLArrayBuffer + { + /// + /// Gets the view's size in bytes. + /// + ulong Size { get; } + + /// + /// Copies bytes from the view into the specified byte array. + /// + /// The offset within the view of the first byte to copy. + /// The maximum number of bytes to copy. + /// The byte array into which to copy the bytes. + /// The index within at which to store the first copied byte. + /// The number of bytes copied. + ulong ReadBytes(ulong offset, ulong count, byte[] destination, ulong destinationIndex); + + /// + /// Copies bytes from the specified byte array into the view. + /// + /// The byte array from which to copy the bytes. + /// The index within of the first byte to copy. + /// The maximum number of bytes to copy. + /// The offset within the view at which to store the first copied byte. + /// The number of bytes copied. + ulong WriteBytes(byte[] source, ulong sourceIndex, ulong count, ulong offset); + + /// + /// Invokes a delegate that returns no value, giving it direct access to the view's contents. + /// + /// The delegate to invoke. + /// + /// This method invokes the specified delegate, passing in the memory address of the view's + /// contents. This memory address is valid only while the delegate is executing. The + /// delegate must not access memory outside the view's range. + /// + void InvokeWithDirectAccess(Action action); + + /// + /// Invokes a delegate that returns a value, giving it direct access to the view's contents. + /// + /// The delegate's return type. + /// The delegate to invoke. + /// The delegate's return value. + /// + /// This method invokes the specified delegate, passing in the memory address of the view's + /// contents. This memory address is valid only while the delegate is executing. The + /// delegate must not access memory outside the view's range. + /// + TResult InvokeWithDirectAccess(Func func); + + } +} diff --git a/Explorer/Assets/DCL/Infrastructure/Utility/IDCLArrayBuffer.cs.meta b/Explorer/Assets/DCL/Infrastructure/Utility/IDCLArrayBuffer.cs.meta new file mode 100644 index 00000000000..f030f7a2352 --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/Utility/IDCLArrayBuffer.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a61d03d67d91d498da6982817d980fc9 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Infrastructure/Utility/IDCLTypedArray.cs b/Explorer/Assets/DCL/Infrastructure/Utility/IDCLTypedArray.cs new file mode 100644 index 00000000000..305307f560d --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/Utility/IDCLTypedArray.cs @@ -0,0 +1,38 @@ +using System; + +namespace Utility +{ + public interface IDCLTypedArray where T : unmanaged + { + ulong Length { get; } + ulong Size { get; } + IDCLArrayBuffer ArrayBuffer { get; } + + /// + /// Copies elements from the typed array into the specified array. + /// + /// The index within the typed array of the first element to copy. + /// The maximum number of elements to copy. + /// The array into which to copy the elements. + /// The index within at which to store the first copied element. + /// The number of elements copied. + ulong Read(ulong index, ulong length, T[] destination, ulong destinationIndex); + + /// + /// Invokes the specified delegate with direct access to the typed array's underlying memory. + /// + /// The delegate to invoke with a pointer to the typed array's memory. + void InvokeWithDirectAccess(Action action); + + /// + /// Invokes the specified delegate with direct access to the typed array's underlying memory. + /// + /// The delegate to invoke with a pointer to the typed array's memory. + /// The return value of the delegate. + int InvokeWithDirectAccess(Func func); + + void ReadBytes(ulong @ulong, ulong eventBytesLength, byte[] eventBytes, ulong ulong1); + + void WriteBytes(byte[] resultArray, ulong @ulong, ulong resultLength, ulong ulong1); + } +} diff --git a/Explorer/Assets/DCL/Infrastructure/Utility/IDCLTypedArray.cs.meta b/Explorer/Assets/DCL/Infrastructure/Utility/IDCLTypedArray.cs.meta new file mode 100644 index 00000000000..56db6349aca --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/Utility/IDCLTypedArray.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ccf29430a8a984e8ead870e34f005b21 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Infrastructure/Utility/Multithreading/DCLTask.cs b/Explorer/Assets/DCL/Infrastructure/Utility/Multithreading/DCLTask.cs index d1a19e2bacd..a55b4e10691 100644 --- a/Explorer/Assets/DCL/Infrastructure/Utility/Multithreading/DCLTask.cs +++ b/Explorer/Assets/DCL/Infrastructure/Utility/Multithreading/DCLTask.cs @@ -57,7 +57,7 @@ public static UniTask RunOnThreadPool( public static UniTask Delay( int sleepMS, CancellationToken cancellationToken = default) => - UniTask.Delay(sleepMS, cancellationToken); + UniTask.Delay(sleepMS, cancellationToken: cancellationToken); #else public static System.Threading.Tasks.Task Delay( // IGNORE_LINE_WEBGL_SYSTEM_TASKS_SAFETY_FLAG int sleepMS, diff --git a/Explorer/Assets/DCL/Infrastructure/Utility/TypedArrayUtils.cs b/Explorer/Assets/DCL/Infrastructure/Utility/TypedArrayUtils.cs new file mode 100644 index 00000000000..7242c4af86a --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/Utility/TypedArrayUtils.cs @@ -0,0 +1,60 @@ +using System; +using Unity.Collections; + +namespace Utility +{ + public static class TypedArrayUtils + { + public static void Write(this IDCLTypedArray array, + NativeArray.ReadOnly source, ulong length, ulong index) where T : unmanaged + { + ulong totalLength = array.Length; + + if (index >= totalLength) { throw new ArgumentOutOfRangeException(nameof(index)); } + +#if UNITY_WEBGL + WriteBytesManaged(array, source.AsReadOnlySpan(), length, index, totalLength); +#else + array.InvokeWithDirectAccess(pData => WriteByteArrayToUnmanagedMemory(source.AsReadOnlySpan(), (int)Math.Min(length, totalLength - index), array.GetPtrWithIndex(pData, index))); +#endif + } + +#if !UNITY_WEBGL + + private static IntPtr GetPtrWithIndex(this IDCLTypedArray array, IntPtr pData, + ulong index) where T : unmanaged + { + var baseAddress = unchecked((ulong)pData.ToInt64()); + return new IntPtr(unchecked((long)checked(baseAddress + (index * (array.Size / array.Length))))); + } + + private static unsafe void WriteByteArrayToUnmanagedMemory(ReadOnlySpan span, int length, IntPtr pDestination) where T : unmanaged + { + length = Math.Min(length, span.Length); + span.CopyTo(new Span(pDestination.ToPointer(), length)); + } +#endif + +#if UNITY_WEBGL + /// + /// WebGL path for writing a span of bytes into a typed array at a given element index. + /// + /// On WebGL, cannot be used because the + /// runtime has no direct unmanaged memory access from the managed side. Instead, the target + /// byte offset is computed from and the array's bytes-per-element ratio, + /// and the data is handed off to which marshals it + /// through managed memory into the underlying JS typed array. + /// + /// + private static void WriteBytesManaged(IDCLTypedArray array, ReadOnlySpan srcSpan, + ulong length, ulong index, ulong totalLength) where T : unmanaged + { + ulong bytesPerElement = array.Size / totalLength; + ulong byteOffset = index * bytesPerElement; + int maxBytes = (int)Math.Min(length, totalLength - index) * (int)bytesPerElement; + int bytesToCopy = Math.Min(maxBytes, srcSpan.Length); + array.WriteBytes(srcSpan[..bytesToCopy].ToArray(), 0, (ulong)bytesToCopy, byteOffset); + } +#endif + } +} diff --git a/Explorer/Assets/DCL/Infrastructure/Utility/TypedArrayUtils.cs.meta b/Explorer/Assets/DCL/Infrastructure/Utility/TypedArrayUtils.cs.meta new file mode 100644 index 00000000000..28727b7709a --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/Utility/TypedArrayUtils.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3a07d9c1879cf47ff8eedad7179a8340 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Infrastructure/Utility/Utility.asmdef b/Explorer/Assets/DCL/Infrastructure/Utility/Utility.asmdef index 4d12bb074bb..60e04a308c0 100644 --- a/Explorer/Assets/DCL/Infrastructure/Utility/Utility.asmdef +++ b/Explorer/Assets/DCL/Infrastructure/Utility/Utility.asmdef @@ -14,7 +14,8 @@ "GUID:9887bf5401cdc9140916d3edbea10b69", "GUID:ba053ae967dabc94a811350e36a486f3", "GUID:3c7b57a14671040bd8c549056adc04f5", - "GUID:2efe6c760f205482eb96395a31bb7036" + "GUID:2efe6c760f205482eb96395a31bb7036", + "GUID:7b6e9563a63f841eaa5b0be900acaa02" ], "includePlatforms": [], "excludePlatforms": [], @@ -36,4 +37,4 @@ "defineConstraints": [], "versionDefines": [], "noEngineReferences": false -} +} \ No newline at end of file diff --git a/Explorer/Assets/DCL/Infrastructure/WebGLEditor.cs b/Explorer/Assets/DCL/Infrastructure/WebGLEditor.cs new file mode 100644 index 00000000000..4997726db6c --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/WebGLEditor.cs @@ -0,0 +1,235 @@ +#if UNITY_EDITOR + +using System.IO; +using System; +using System.Diagnostics; +using System.Threading; +using UnityEditor; +using UnityEditor.Build.Reporting; +using UnityEngine; +using UnityEngine.Assertions; +using System.Reflection; + +using DCL.Diagnostics; + +namespace DCL.Infrastructure +{ + public static class WebGLEditor + { + public const string CHROME_PID_FILE = "chrome_pid.txt"; + public const string SERVER_PID_FILE = "server_pid.txt"; + public const string BUILDS_FOLDER = "Builds/WebGL"; + + [MenuItem("Decentraland/WebGL/Build and Start")] + public static void BuildAndStart() + { +#if UNITY_EDITOR_WIN + const string CHROME_PATH = @"C:\Program Files\Google\Chrome\Application\chrome.exe"; + const string CHROME_TEMP_DIR = "C:/tmp"; +#else + const string CHROME_PATH = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; + const string CHROME_TEMP_DIR = "/tmp/chrome-no-cors"; +#endif + const string CHROME_ARGS = "--disable-web-security --user-data-dir=" + CHROME_TEMP_DIR; + + const int START_PORT = 8000; + const string SERVER_CMD = "python3 -m http.server"; // EXAMPLE: python3 -m http.server 8044 + const string INDEX_BUILD_PATH = "webgl_build_increment_index.txt"; + + ReportHub.Log(ReportCategory.UNSPECIFIED, $"Build and Start: Begin"); + + // To free memory + KillProcesses(); + + ReadAndIncrementBuildIndex(INDEX_BUILD_PATH, out int incrementedIndex); + + int targetPort = START_PORT + incrementedIndex; + string buildFolder = $"{BUILDS_FOLDER}/{incrementedIndex}"; + + Build(buildFolder); + StartServer(buildFolder, targetPort, SERVER_PID_FILE); + StartChrome(CHROME_PATH, CHROME_ARGS, targetPort, CHROME_PID_FILE); + + ReportHub.Log(ReportCategory.UNSPECIFIED, $"Build and Start: Finished"); + } + + [MenuItem("Decentraland/WebGL/Stop Server and Browser")] + public static void StopServerAndBrowser() + { + KillProcesses(); + ReportHub.Log(ReportCategory.UNSPECIFIED, "StopServerAndBrowser finished"); + } + + [MenuItem("Decentraland/WebGL/Clear Builds Directory")] + public static void ClearBuildsDirectory() + { + if (Directory.Exists(BUILDS_FOLDER)) + { + Directory.Delete(BUILDS_FOLDER, true); + } + + ReportHub.Log(ReportCategory.UNSPECIFIED, "ClearBuildsDirectory finished"); + } + + private static void KillProcesses() + { + Process_Kill(SERVER_PID_FILE); + Process_Kill(CHROME_PID_FILE); + } + + private static void Build(string buildFolder) + { + Directory.CreateDirectory(buildFolder); + + EditorUtility.DisplayDialog( + "WebGL Build", + "You will be promted by Unity to select the build location. Select any since it will be ignored.", + "OK" + ); + + BuildPlayerOptions buildOptions = BuildPlayerWindow.DefaultBuildMethods.GetBuildPlayerOptions(new BuildPlayerOptions()); + buildOptions.locationPathName = buildFolder; + + BuildReport report = BuildPipeline.BuildPlayer(buildOptions); + + if (report.summary.result != BuildResult.Succeeded) + throw new Exception("WebGL build failed"); + + ReportHub.Log(ReportCategory.UNSPECIFIED, $"WebGL build completed at the path: {buildFolder}"); + } + + private static BuildPlayerOptions GetBuildPlayerOptions( + bool askForLocation = false, + BuildPlayerOptions defaultOptions = new BuildPlayerOptions()) + { + // Get static internal "GetBuildPlayerOptionsInternal" method + MethodInfo method = typeof(BuildPlayerWindow).GetMethod( + "GetBuildPlayerOptionsInternal", + BindingFlags.NonPublic | BindingFlags.Static); + + // TODO it won't work for some reason. Seems Unity changed their internal API. + // https://discussions.unity.com/t/getting-the-current-buildoptions/224799/2 + Assert.IsNotNull(method, "Cannot get method GetBuildPlayerOptionsInternal"); + + // invoke internal method + object result = method.Invoke( + null, + new object[] { askForLocation, defaultOptions}); + + Assert.IsNotNull(result); + + return (BuildPlayerOptions)result; + } + + private static void StartChrome(string chromePath, string chromeArgs, int port, string pidSavePath) + { + Process_Kill(pidSavePath); + + string url = $"http://localhost:{port}"; + + Process ChromeProcess = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = chromePath, + Arguments = $"{chromeArgs} {url}", + UseShellExecute = false + } + }; + + ChromeProcess.Start(); + Process_WritePid(ChromeProcess.Id, pidSavePath); + } + + private static void StartServer(string buildFolder, int port, string pidSavePath) + { + Process_Kill(pidSavePath); + + Process ServerProcess = new Process + { + StartInfo = new ProcessStartInfo + { + +#if UNITY_EDITOR_WIN + FileName = "python3.exe", + Arguments = $"-m http.server {port}", +#else + FileName = "/usr/bin/env", + Arguments = $"python3 -m http.server {port}", +#endif + + WorkingDirectory = Path.GetFullPath(buildFolder), + RedirectStandardOutput = false, + RedirectStandardError = false, + UseShellExecute = false + } + }; + + ServerProcess.Start(); + Process_WritePid(ServerProcess.Id, pidSavePath); + } + + // Domain reload drops static fields + private static void Process_WritePid(int pid, string pidSavePath) + { + string directory = Path.GetDirectoryName(pidSavePath); + if (!string.IsNullOrEmpty(directory)) + Directory.CreateDirectory(directory); + + File.WriteAllText(pidSavePath, pid.ToString()); + } + + private static void Process_Kill(string pidSavePath) + { + if (!File.Exists(pidSavePath)) + return; + + string content = File.ReadAllText(pidSavePath); + + if (!int.TryParse(content, out int pid)) + { + File.Delete(pidSavePath); + return; + } + + try + { + Process process = Process.GetProcessById(pid); + + if (!process.HasExited) + { + process.Kill(); // kill entire process tree + process.WaitForExit(2000); + } + } + catch (ArgumentException) + { + // Process already exited + } + catch (Exception ex) + { + UnityEngine.Debug.LogWarning($"Failed to kill PID {pid}: {ex.Message}"); + } + + File.Delete(pidSavePath); + } + + private static string[] GetEnabledScenes() + { + return Array.ConvertAll(EditorBuildSettings.scenes,s => s.path); + } + + private static void ReadAndIncrementBuildIndex(string path, out int incrementedIndex) + { + incrementedIndex = 0; + + if (File.Exists(path)) + int.TryParse(File.ReadAllText(path), out incrementedIndex); + + incrementedIndex++; + File.WriteAllText(path, incrementedIndex.ToString()); + } + } +} + +#endif diff --git a/Explorer/Assets/DCL/Infrastructure/WebGLEditor.cs.meta b/Explorer/Assets/DCL/Infrastructure/WebGLEditor.cs.meta new file mode 100644 index 00000000000..2397a8772f0 --- /dev/null +++ b/Explorer/Assets/DCL/Infrastructure/WebGLEditor.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 0c3f16a1e788a4fb6b0f52fcf4ba1929 \ No newline at end of file diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/VoiceChatActivatableConnectiveRoom.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/VoiceChatActivatableConnectiveRoom.cs index f780b2ab392..db70ef6cb33 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/VoiceChatActivatableConnectiveRoom.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/VoiceChatActivatableConnectiveRoom.cs @@ -1,6 +1,5 @@ using Cysharp.Threading.Tasks; using DCL.Diagnostics; -using DCL.Multiplayer.Connections.Audio; using DCL.Multiplayer.Connections.Credentials; using DCL.Multiplayer.Connections.Rooms; using DCL.Multiplayer.Connections.Rooms.Connective; @@ -11,10 +10,14 @@ using LiveKit.Rooms.DataPipes; using LiveKit.Rooms.Info; using LiveKit.Rooms.Participants; + +#if !UNITY_WEBGL using LiveKit.Rooms.Participants.Factory; using LiveKit.Rooms.Streaming.Audio; using LiveKit.Rooms.TrackPublications; using LiveKit.Rooms.Tracks.Factory; +#endif + using LiveKit.Rooms.VideoStreaming; using RichTypes; using System; @@ -192,6 +195,8 @@ private async UniTask TryConnectToRoomAsync(CancellationToken ct) private static IRoom CreateFreshRoom() { + +#if !UNITY_WEBGL var hub = new ParticipantsHub(); var newRoom = new Room( @@ -208,6 +213,9 @@ private static IRoom CreateFreshRoom() new AudioStreams(hub), null! ); +#else + Room newRoom = new Room(); +#endif return new LogRoom(newRoom, "VoiceChat"); } diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Demo/ArchipelagoRoomPlayground.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Demo/ArchipelagoRoomPlayground.cs index d2895aeef3a..f853c8b134b 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Demo/ArchipelagoRoomPlayground.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Demo/ArchipelagoRoomPlayground.cs @@ -5,7 +5,9 @@ using DCL.Multiplayer.Connections.Archipelago.AdapterAddress.Current; using DCL.Multiplayer.Connections.Archipelago.Rooms; using DCL.Multiplayer.Connections.DecentralandUrls; +#if !UNITY_WEBGL using DCL.Multiplayer.Connections.FfiClients; +#endif using DCL.Multiplayer.Connections.Pools; using DCL.RealmNavigation; using DCL.Utility; @@ -33,6 +35,7 @@ private void Start() private async UniTaskVoid LaunchAsync() { +#if !UNITY_WEBGL IFFIClient.Default.EnsureInitialize(); var world = World.Create(); @@ -64,6 +67,9 @@ private async UniTaskVoid LaunchAsync() system.Update(UnityEngine.Time.deltaTime); await UniTask.Yield(); } +#else + await UniTask.CompletedTask; +#endif } } } diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Demo/GateKeeperRoomPlayground.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Demo/GateKeeperRoomPlayground.cs index b78c9f8d3be..cbf493ffe6f 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Demo/GateKeeperRoomPlayground.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Demo/GateKeeperRoomPlayground.cs @@ -4,7 +4,9 @@ using DCL.Character.Components; using DCL.DebugUtilities.UIBindings; using DCL.Multiplayer.Connections.DecentralandUrls; +#if !UNITY_WEBGL using DCL.Multiplayer.Connections.FfiClients; +#endif using DCL.Multiplayer.Connections.GateKeeper.Meta; using DCL.Multiplayer.Connections.GateKeeper.Rooms; using DCL.Multiplayer.Connections.GateKeeper.Rooms.Options; @@ -32,7 +34,7 @@ private void Start() private async UniTaskVoid LaunchAsync() { -#if UNITY_EDITOR +#if UNITY_EDITOR && !UNITY_WEBGL IFFIClient.Default.EnsureInitialize(); var world = World.Create(); diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Demo/LocalSceneDevelopmentPlayground.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Demo/LocalSceneDevelopmentPlayground.cs index 619337a5eef..0767303c93b 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Demo/LocalSceneDevelopmentPlayground.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Demo/LocalSceneDevelopmentPlayground.cs @@ -4,7 +4,9 @@ using DCL.Character.Components; using DCL.DebugUtilities.UIBindings; using DCL.Multiplayer.Connections.DecentralandUrls; +#if !UNITY_WEBGL using DCL.Multiplayer.Connections.FfiClients; +#endif using DCL.Multiplayer.Connections.GateKeeper.Meta; using DCL.Multiplayer.Connections.GateKeeper.Rooms; using DCL.Multiplayer.Connections.GateKeeper.Rooms.Options; @@ -31,7 +33,7 @@ private void Start() private async UniTaskVoid LaunchAsync() { -#if UNITY_EDITOR +#if UNITY_EDITOR && !UNITY_WEBGL IFFIClient.Default.EnsureInitialize(); var world = World.Create(); diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/FfiClients/FfiClientExtensions.cs b/Explorer/Assets/DCL/Multiplayer/Connections/FfiClients/FfiClientExtensions.cs index 93033639fed..45d0f80d9ac 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/FfiClients/FfiClientExtensions.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/FfiClients/FfiClientExtensions.cs @@ -1,3 +1,5 @@ +#if !UNITY_WEBGL || UNITY_EDITOR + using DCL.Diagnostics; using LiveKit.Internal.FFIClients; @@ -5,7 +7,7 @@ namespace DCL.Multiplayer.Connections.FfiClients { public static class FfiClientExtensions { - public static void EnsureInitialize(this IFFIClient ffiClient) + public static void EnsureInitialize(this global::LiveKit.Internal.FFIClients.IFFIClient ffiClient) { bool initialized = ffiClient.Initialized(); ReportHub.Log(ReportData.UNSPECIFIED, $"FfiClient initilized: {initialized}"); @@ -19,3 +21,5 @@ public static void EnsureInitialize(this IFFIClient ffiClient) } } } + +#endif diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/ConnectiveRoom.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/ConnectiveRoom.cs index 3b63fe3a1f3..a5373bf5195 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/ConnectiveRoom.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Connective/ConnectiveRoom.cs @@ -11,10 +11,6 @@ using LiveKit.Rooms.DataPipes; using LiveKit.Rooms.Info; using LiveKit.Rooms.Participants; -using LiveKit.Rooms.Participants.Factory; -using LiveKit.Rooms.Streaming.Audio; -using LiveKit.Rooms.TrackPublications; -using LiveKit.Rooms.Tracks.Factory; using LiveKit.Rooms.VideoStreaming; using RichTypes; using System; @@ -24,6 +20,13 @@ using Utility.Multithreading; using DCL.LiveKit.Public; +#if !UNITY_WEBGL || UNITY_EDITOR +using LiveKit.Rooms.Participants.Factory; +using LiveKit.Rooms.Streaming.Audio; +using LiveKit.Rooms.TrackPublications; +using LiveKit.Rooms.Tracks.Factory; +#endif + namespace DCL.Multiplayer.Connections.Rooms.Connective { public enum RoomSelection : byte @@ -78,6 +81,7 @@ protected ConnectiveRoom() { ParticipantsHub hub = new (); +#if !UNITY_WEBGL // Pass null for AudioTracks - Room constructor will create it automatically Room origin = new Room( new ArrayMemoryPool(), @@ -93,6 +97,9 @@ protected ConnectiveRoom() new AudioStreams(hub), null ); +#else + Room origin = new Room(); +#endif return new LogRoom(origin, logPrefix); }); diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Interior/InteriorLocalTracks.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Interior/InteriorLocalTracks.cs index 0431ee11c3d..7667e683103 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Interior/InteriorLocalTracks.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Interior/InteriorLocalTracks.cs @@ -1,3 +1,5 @@ +#if !UNITY_WEBGL || UNITY_EDITOR + using DCL.Multiplayer.Connections.Rooms.Nulls; using LiveKit; using LiveKit.Rooms.Tracks; @@ -24,3 +26,5 @@ public void Assign(ILocalTracks value, out ILocalTracks? previous) } } } + +#endif diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Logs/LogLocalTracks.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Logs/LogLocalTracks.cs index e05b1cbc17b..292ca65011b 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Logs/LogLocalTracks.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Logs/LogLocalTracks.cs @@ -1,3 +1,5 @@ +#if !UNITY_WEBGL || UNITY_EDITOR + using DCL.Diagnostics; using LiveKit; using LiveKit.Rooms.Tracks; @@ -47,3 +49,5 @@ public ITrack CreateVideoTrack(string name, RtcVideoSource source) } } } + +#endif diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Logs/LogVideoStreams.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Logs/LogVideoStreams.cs index ed8d566f711..1ee2e098c48 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Logs/LogVideoStreams.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Logs/LogVideoStreams.cs @@ -1,3 +1,5 @@ +#if !UNITY_WEBGL || UNITY_EDITOR + using DCL.Diagnostics; using LiveKit.Rooms; using LiveKit.Rooms.Streaming; @@ -69,3 +71,5 @@ public class LogAudioStreams : LogStreams, IAudioS public LogAudioStreams(IStreams origin) : base(origin, nameof(LogVideoStreams)) { } } } + +#endif diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Nulls/NullParticipantsHub.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Nulls/NullParticipantsHub.cs index 8eeb94113da..c619f0e64d9 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Nulls/NullParticipantsHub.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Nulls/NullParticipantsHub.cs @@ -10,7 +10,9 @@ public class NullParticipantsHub : IParticipantsHub public static readonly NullParticipantsHub INSTANCE = new (); public static readonly LKParticipant NULL_PARTICIPANT = new (); +#if !UNITY_WEBGL || UNITY_EDITOR public static readonly WeakReference WEAK_NULL_PARTICIPANT = new (NULL_PARTICIPANT); +#endif public event ParticipantDelegate? UpdatesFromParticipant; diff --git a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Nulls/NullVideoStreams.cs b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Nulls/NullVideoStreams.cs index 96d51aa56a7..30f8e729f82 100644 --- a/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Nulls/NullVideoStreams.cs +++ b/Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Nulls/NullVideoStreams.cs @@ -1,3 +1,5 @@ +#if !UNITY_WEBGL || UNITY_EDITOR + using LiveKit.Rooms; using LiveKit.Rooms.Streaming; using LiveKit.Rooms.Streaming.Audio; @@ -46,3 +48,5 @@ public class NullAudioStreams : NullStreams, IAudi public static readonly NullAudioStreams INSTANCE = new (); } } + +#endif diff --git a/Explorer/Assets/DCL/PluginSystem/Global/GlobalGLTFLoadingPlugin.cs b/Explorer/Assets/DCL/PluginSystem/Global/GlobalGLTFLoadingPlugin.cs index a8dfc24f034..b411e4e04ca 100644 --- a/Explorer/Assets/DCL/PluginSystem/Global/GlobalGLTFLoadingPlugin.cs +++ b/Explorer/Assets/DCL/PluginSystem/Global/GlobalGLTFLoadingPlugin.cs @@ -1,3 +1,7 @@ +// GLTFast forces usage of Task that is not compatible with WebGL +// TRUST_WEBGL_SYSTEM_TASKS_SAFETY_FLAG +#if !UNITY_WEBGL + using Arch.SystemGroups; using DCL.WebRequests; using ECS; @@ -55,3 +59,5 @@ public void InjectToWorld(ref ArchSystemsWorldBuilder builder, } } } + +#endif diff --git a/Explorer/Assets/DCL/PluginSystem/Global/MultiplayerPlugin.cs b/Explorer/Assets/DCL/PluginSystem/Global/MultiplayerPlugin.cs index f2a3c6d0b50..5142355d9e6 100644 --- a/Explorer/Assets/DCL/PluginSystem/Global/MultiplayerPlugin.cs +++ b/Explorer/Assets/DCL/PluginSystem/Global/MultiplayerPlugin.cs @@ -6,7 +6,9 @@ using DCL.Character; using DCL.DebugUtilities; using DCL.Multiplayer.Connections.Archipelago.Rooms; +#if !UNITY_WEBGL || UNITY_EDITOR using DCL.Multiplayer.Connections.FfiClients; +#endif using DCL.Multiplayer.Connections.GateKeeper.Rooms; using DCL.Multiplayer.Connections.Messaging.Hubs; using DCL.Multiplayer.Connections.RoomHubs; @@ -122,7 +124,9 @@ public void Dispose() gateKeeperSceneRoom.Dispose(); #if !NO_LIVEKIT_MODE +#if !UNITY_WEBGL || UNITY_EDITOR IFFIClient.Default.Dispose(); +#endif #endif } @@ -135,7 +139,10 @@ public async UniTask InitializeAsync(Settings settings, CancellationToken ct) public void InjectToWorld(ref ArchSystemsWorldBuilder builder, in GlobalPluginArguments globalPluginArguments) { #if !NO_LIVEKIT_MODE + +#if !UNITY_WEBGL || UNITY_EDITOR IFFIClient.Default.EnsureInitialize(); +#endif DebugRoomsSystem.InjectToWorld(ref builder, roomsStatus, archipelagoIslandRoom, gateKeeperSceneRoom, chatRoom, voiceChatRoom, entityParticipantTable, remoteMetadata, debugContainerBuilder); DebugThroughputRoomsSystem.InjectToWorld(ref builder, roomHub, debugContainerBuilder, islandThroughputBufferBunch, sceneThroughputBufferBunch); diff --git a/Explorer/Assets/DCL/PluginSystem/World/Dependencies/ECSWorldInstanceSharedDependencies.cs b/Explorer/Assets/DCL/PluginSystem/World/Dependencies/ECSWorldInstanceSharedDependencies.cs index edf687ed814..c3b177540bf 100644 --- a/Explorer/Assets/DCL/PluginSystem/World/Dependencies/ECSWorldInstanceSharedDependencies.cs +++ b/Explorer/Assets/DCL/PluginSystem/World/Dependencies/ECSWorldInstanceSharedDependencies.cs @@ -23,9 +23,11 @@ public readonly struct ECSWorldInstanceSharedDependencies public readonly IEntityCollidersGlobalCache EntityCollidersGlobalCache; public readonly ISceneStateProvider SceneStateProvider; public readonly EntityEventsBuilder EntityEventsBuilder; - public readonly MultiThreadSync MultiThreadSync; public readonly ISystemGroupsUpdateGate EcsGroupThrottler; public readonly ISystemsUpdateGate EcsSystemsGate; +#if !UNITY_WEBGL + public readonly MultiThreadSync MultiThreadSync; +#endif public ECSWorldInstanceSharedDependencies( ISceneData sceneData, @@ -36,13 +38,17 @@ public ECSWorldInstanceSharedDependencies( IEntityCollidersSceneCache entityCollidersSceneCache, IEntityCollidersGlobalCache entityCollidersGlobalCache, ISceneStateProvider sceneStateProvider, EntityEventsBuilder entityEventsBuilder, +#if !UNITY_WEBGL MultiThreadSync multiThreadSync, +#endif ISystemGroupsUpdateGate ecsGroupThrottler, ISystemsUpdateGate ecsSystemsGate) { SceneData = sceneData; EcsToCRDTWriter = ecsToCRDTWriter; EntitiesMap = entitiesMap; +#if !UNITY_WEBGL MultiThreadSync = multiThreadSync; +#endif ScenePartition = scenePartition; SceneStateProvider = sceneStateProvider; SceneExceptionsHandler = sceneExceptionsHandler; diff --git a/Explorer/Assets/DCL/PluginSystem/World/Dependencies/EmptyScenesWorldSharedDependencies.cs b/Explorer/Assets/DCL/PluginSystem/World/Dependencies/EmptyScenesWorldSharedDependencies.cs index c54c252b00c..67b0711b4f3 100644 --- a/Explorer/Assets/DCL/PluginSystem/World/Dependencies/EmptyScenesWorldSharedDependencies.cs +++ b/Explorer/Assets/DCL/PluginSystem/World/Dependencies/EmptyScenesWorldSharedDependencies.cs @@ -11,15 +11,27 @@ public readonly struct EmptyScenesWorldSharedDependencies public readonly Dictionary FakeEntitiesMap; public readonly Entity SceneRoot; public readonly ISceneData SceneData; + +#if !UNITY_WEBGL public readonly MultiThreadSync MultiThread; +#endif - public EmptyScenesWorldSharedDependencies(Dictionary fakeEntitiesMap, Entity sceneRoot, - ISceneData sceneData, MultiThreadSync multiThreadSync) + public EmptyScenesWorldSharedDependencies( + Dictionary fakeEntitiesMap, + Entity sceneRoot, + ISceneData sceneData +#if !UNITY_WEBGL + , + MultiThreadSync multiThreadSync +#endif + ) { FakeEntitiesMap = fakeEntitiesMap; SceneRoot = sceneRoot; SceneData = sceneData; +#if !UNITY_WEBGL MultiThread = multiThreadSync; +#endif } } } diff --git a/Explorer/Assets/DCL/PluginSystem/World/GltfContainerPlugin.cs b/Explorer/Assets/DCL/PluginSystem/World/GltfContainerPlugin.cs index b22947c7cb2..146e2ed56f1 100644 --- a/Explorer/Assets/DCL/PluginSystem/World/GltfContainerPlugin.cs +++ b/Explorer/Assets/DCL/PluginSystem/World/GltfContainerPlugin.cs @@ -1,3 +1,7 @@ +// GLTFast forces usage of Task that is not compatible with WebGL +// TRUST_WEBGL_SYSTEM_TASKS_SAFETY_FLAG +#if !UNITY_WEBGL + using Arch.SystemGroups; using Cysharp.Threading.Tasks; using DCL.Ipfs; @@ -140,3 +144,5 @@ public UniTask InitializeAsync(NoExposedPluginSettings settings, CancellationTok UniTask.CompletedTask; } } + +#endif diff --git a/Explorer/Assets/DCL/Prefs/DCLPlayerPrefs.cs b/Explorer/Assets/DCL/Prefs/DCLPlayerPrefs.cs index a0d74a432b2..fb30d425381 100644 --- a/Explorer/Assets/DCL/Prefs/DCLPlayerPrefs.cs +++ b/Explorer/Assets/DCL/Prefs/DCLPlayerPrefs.cs @@ -124,7 +124,11 @@ private static void Initialize(bool inMemory) if (dclPrefs != null) throw new InvalidOperationException("DCLPrefs already initialized."); +#if UNITY_WEBGL + dclPrefs = new LocalStorageDCLPlayerPrefs(); // FileDCLPlayerPrefs is not available in WebGL; persist via browser localStorage +#else dclPrefs = inMemory ? new InMemoryDCLPlayerPrefs() : new FileDCLPlayerPrefs(); +#endif Application.quitting += OnQuitting; } diff --git a/Explorer/Assets/DCL/Prefs/LocalStorageDCLPlayerPrefs.cs b/Explorer/Assets/DCL/Prefs/LocalStorageDCLPlayerPrefs.cs new file mode 100644 index 00000000000..d0be219fb75 --- /dev/null +++ b/Explorer/Assets/DCL/Prefs/LocalStorageDCLPlayerPrefs.cs @@ -0,0 +1,175 @@ +#if UNITY_WEBGL +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace DCL.Prefs +{ + /// + /// implementation for WebGL that persists all data in the browser's + /// localStorage as a single JSON blob under the key dcl_prefs. + /// Mirrors the UserData structure of . + /// is called automatically on every Set* operation so that data + /// is not lost if the browser tab is closed without an explicit flush. + /// + public class LocalStorageDCLPlayerPrefs : IDCLPrefs + { + private readonly UserData userData; + + private byte[] encodeBuffer = Array.Empty(); + private IntPtr nativeBuffer = IntPtr.Zero; + private int nativeCapacity; + + public LocalStorageDCLPlayerPrefs() + { + userData = Load(); + } + + ~LocalStorageDCLPlayerPrefs() + { + if (nativeBuffer != IntPtr.Zero) + Marshal.FreeHGlobal(nativeBuffer); + } + + public void SetString(string key, string value) + { + DeleteKey(key); + userData.Strings[key] = value; + Save(); + } + + public void SetInt(string key, int value) + { + DeleteKey(key); + userData.Ints[key] = value; + Save(); + } + + public void SetFloat(string key, float value) + { + DeleteKey(key); + userData.Floats[key] = value; + Save(); + } + + public void SetBool(string key, bool value) + { + DeleteKey(key); + userData.Bools[key] = value; + Save(); + } + + public string GetString(string key, string defaultValue) => + userData.Strings.GetValueOrDefault(key, defaultValue); + + public int GetInt(string key, int defaultValue) => + userData.Ints.GetValueOrDefault(key, defaultValue); + + public float GetFloat(string key, float defaultValue) => + userData.Floats.GetValueOrDefault(key, defaultValue); + + public bool GetBool(string key, bool defaultValue) => + userData.Bools.GetValueOrDefault(key, defaultValue); + + public bool HasKey(string key) => + userData.Strings.ContainsKey(key) || userData.Ints.ContainsKey(key) || + userData.Floats.ContainsKey(key) || userData.Bools.ContainsKey(key); + + public void DeleteKey(string key) + { + userData.Strings.Remove(key); + userData.Ints.Remove(key); + userData.Floats.Remove(key); + userData.Bools.Remove(key); + } + + public void DeleteAll() + { + userData.Strings.Clear(); + userData.Ints.Clear(); + userData.Floats.Clear(); + userData.Bools.Clear(); + Save(); + } + + public void Save() + { + string json = JsonConvert.SerializeObject(userData); + + int maxBytes = Encoding.UTF8.GetMaxByteCount(json.Length); + + if (encodeBuffer.Length < maxBytes) + encodeBuffer = new byte[maxBytes]; + + int byteCount = Encoding.UTF8.GetBytes(json, 0, json.Length, encodeBuffer, 0); + + // Ensure unmanaged buffer has room for encoded bytes + null terminator. + int required = byteCount + 1; + + if (nativeCapacity < required) + { + if (nativeBuffer != IntPtr.Zero) Marshal.FreeHGlobal(nativeBuffer); + nativeBuffer = Marshal.AllocHGlobal(required); + nativeCapacity = required; + } + + Marshal.Copy(encodeBuffer, 0, nativeBuffer, byteCount); + Marshal.WriteByte(nativeBuffer, byteCount, 0); // null terminator for jslib UTF8ToString + DCLPrefs_Save(nativeBuffer); + } + + /// + /// Synchronous save that blocks until data is written to disk. + /// Use in OnApplicationQuit or other shutdown paths where async save may not complete. + /// + public void SaveSync() => Save(); + + private static UserData Load() + { + int bufferSize = 1024 * 64; + IntPtr ptr = Marshal.AllocHGlobal(bufferSize); + + try + { + int result = DCLPrefs_Load(ptr, bufferSize); + + if (result < 0) + { + bufferSize = -result; + Marshal.FreeHGlobal(ptr); + ptr = Marshal.AllocHGlobal(bufferSize); + result = DCLPrefs_Load(ptr, bufferSize); + } + + if (result > 0) + { + var buffer = new byte[result]; + Marshal.Copy(ptr, buffer, 0, result); + string json = Encoding.UTF8.GetString(buffer); + return JsonConvert.DeserializeObject(json) ?? new UserData(); + } + + return new UserData(); + } + finally { Marshal.FreeHGlobal(ptr); } + } + + [DllImport("__Internal")] + private static extern void DCLPrefs_Save(IntPtr jsonPtr); + + [DllImport("__Internal")] + private static extern int DCLPrefs_Load(IntPtr resultPtr, int resultSize); + + [Serializable] + private class UserData + { + public Dictionary Strings { get; set; } = new (); + public Dictionary Ints { get; set; } = new (); + public Dictionary Floats { get; set; } = new (); + public Dictionary Bools { get; set; } = new (); + } + } +} +#endif diff --git a/Explorer/Assets/DCL/Prefs/LocalStorageDCLPlayerPrefs.cs.meta b/Explorer/Assets/DCL/Prefs/LocalStorageDCLPlayerPrefs.cs.meta new file mode 100644 index 00000000000..ca43cda0d2c --- /dev/null +++ b/Explorer/Assets/DCL/Prefs/LocalStorageDCLPlayerPrefs.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 45f66eee7744f4fcc9fec51fd7d18f0b \ No newline at end of file diff --git a/Explorer/Assets/DCL/Prefs/Tests/FileDCLPlayerPrefsSlotReclamationShould.cs b/Explorer/Assets/DCL/Prefs/Tests/FileDCLPlayerPrefsSlotReclamationShould.cs index 7ae477bafb4..01a219a533c 100644 --- a/Explorer/Assets/DCL/Prefs/Tests/FileDCLPlayerPrefsSlotReclamationShould.cs +++ b/Explorer/Assets/DCL/Prefs/Tests/FileDCLPlayerPrefsSlotReclamationShould.cs @@ -1,3 +1,5 @@ +#if !UNITY_WEBGL + using Newtonsoft.Json; using NUnit.Framework; using System; @@ -180,3 +182,5 @@ private void AssertClaimBelongsToCurrentProcess(int slot) } } } + +#endif diff --git a/Explorer/Assets/DCL/RuntimeDeepLink/DeepLinkSentinel.cs b/Explorer/Assets/DCL/RuntimeDeepLink/DeepLinkSentinel.cs index e7f0b9b3627..3a096992642 100644 --- a/Explorer/Assets/DCL/RuntimeDeepLink/DeepLinkSentinel.cs +++ b/Explorer/Assets/DCL/RuntimeDeepLink/DeepLinkSentinel.cs @@ -1,3 +1,5 @@ +#if !UNITY_WEBGL + using Cysharp.Threading.Tasks; using DCL.Diagnostics; using DCL.Utilities.Extensions; @@ -68,3 +70,5 @@ public static async UniTaskVoid StartListenForDeepLinksAsync(this IDeepLinkHandl } } } + +#endif diff --git a/Explorer/Assets/DCL/RuntimeDeepLink/RuntimeDeepLinkPlayground.cs b/Explorer/Assets/DCL/RuntimeDeepLink/RuntimeDeepLinkPlayground.cs index fd2bd401a8f..eefa51375c7 100644 --- a/Explorer/Assets/DCL/RuntimeDeepLink/RuntimeDeepLinkPlayground.cs +++ b/Explorer/Assets/DCL/RuntimeDeepLink/RuntimeDeepLinkPlayground.cs @@ -6,9 +6,11 @@ public class RuntimeDeepLinkPlayground : MonoBehaviour { private void Start() { +#if !UNITY_WEBGL IDeepLinkHandle.Null.INSTANCE .StartListenForDeepLinksAsync(destroyCancellationToken) .Forget(); +#endif } } } diff --git a/Explorer/Assets/DCL/SDKComponents/AudioAnalysis/AudioAnalysisSystem.cs b/Explorer/Assets/DCL/SDKComponents/AudioAnalysis/AudioAnalysisSystem.cs index ca545c0a49e..aa5faa9d083 100644 --- a/Explorer/Assets/DCL/SDKComponents/AudioAnalysis/AudioAnalysisSystem.cs +++ b/Explorer/Assets/DCL/SDKComponents/AudioAnalysis/AudioAnalysisSystem.cs @@ -40,10 +40,15 @@ IECSToCRDTWriter ecsToCRDTWriter protected override void Update(float t) { +#if !UNITY_WEBGL HandleAudioSourceComponentQuery(World); HandleMediaPlayerComponentQuery(World); +#endif } +// TODO WebGL is not supported +#if !UNITY_WEBGL + [Query] private void HandleMediaPlayerComponent( CRDTEntity entity, @@ -110,5 +115,6 @@ ref PBAudioAnalysis sdkComponent ecsToCRDTWriter.PutMessage(sdkComponent, entity); } } +#endif } } diff --git a/Explorer/Assets/DCL/SDKComponents/MediaStream/Components/OpenMediaPromise.cs b/Explorer/Assets/DCL/SDKComponents/MediaStream/Components/OpenMediaPromise.cs index 61c4d4f5e0e..c996eced54b 100644 --- a/Explorer/Assets/DCL/SDKComponents/MediaStream/Components/OpenMediaPromise.cs +++ b/Explorer/Assets/DCL/SDKComponents/MediaStream/Components/OpenMediaPromise.cs @@ -35,12 +35,14 @@ public async UniTask UrlReachabilityResolveAsync(MediaAddress newMediaAddress, R this.mediaAddress = newMediaAddress; this.originalAddress = newMediaAddress; +#if !UNITY_WEBGL if (mediaAddress.IsLivekitAddress(out _)) { isReachable = true; status = Status.Resolved; return; } +#endif mediaAddress.IsUrlMediaAddress(out var urlMediaAddress); string url = urlMediaAddress!.Url; diff --git a/Explorer/Assets/DCL/SDKComponents/MediaStream/LivekitAddress.cs b/Explorer/Assets/DCL/SDKComponents/MediaStream/LivekitAddress.cs index d85357f7eaa..7fa6df88017 100644 --- a/Explorer/Assets/DCL/SDKComponents/MediaStream/LivekitAddress.cs +++ b/Explorer/Assets/DCL/SDKComponents/MediaStream/LivekitAddress.cs @@ -1,3 +1,5 @@ +#if !UNITY_WEBGL + using REnum; using System; @@ -51,3 +53,5 @@ public static LivekitAddress New(string rawAddress) } } } + +#endif diff --git a/Explorer/Assets/DCL/SDKComponents/MediaStream/LivekitPlayer.cs b/Explorer/Assets/DCL/SDKComponents/MediaStream/LivekitPlayer.cs index c6cc06a4c4d..346dd73b7c5 100644 --- a/Explorer/Assets/DCL/SDKComponents/MediaStream/LivekitPlayer.cs +++ b/Explorer/Assets/DCL/SDKComponents/MediaStream/LivekitPlayer.cs @@ -1,3 +1,6 @@ +// Currently media-streams are not supported for webgl +#if !UNITY_WEBGL + using DCL.Diagnostics; using DCL.Optimization.ThreadSafePool; using DCL.SDKComponents.MediaStream; @@ -426,3 +429,5 @@ public bool IsFromPresentationBot() } } } + +#endif diff --git a/Explorer/Assets/DCL/SDKComponents/MediaStream/MediaAddress.cs b/Explorer/Assets/DCL/SDKComponents/MediaStream/MediaAddress.cs index fcbb949ef02..824f7d9cc98 100644 --- a/Explorer/Assets/DCL/SDKComponents/MediaStream/MediaAddress.cs +++ b/Explorer/Assets/DCL/SDKComponents/MediaStream/MediaAddress.cs @@ -18,20 +18,26 @@ public override string ToString() => [REnum] [REnumField(typeof(UrlMediaAddress))] +#if !UNITY_WEBGL [REnumField(typeof(LivekitAddress))] +#endif public partial struct MediaAddress { public bool IsEmpty => Match( - onUrlMediaAddress: static address => string.IsNullOrEmpty(address.Url), - onLivekitAddress: static address => address.IsEmpty + onUrlMediaAddress: static address => string.IsNullOrEmpty(address.Url) +#if !UNITY_WEBGL + , onLivekitAddress: static address => address.IsEmpty +#endif ); public static MediaAddress New(string rawAddress) { +#if !UNITY_WEBGL if (rawAddress.IsLivekitAddress()) { return FromLivekitAddress(LivekitAddress.New(rawAddress)); } +#endif return FromUrlMediaAddress(new UrlMediaAddress(rawAddress)); } diff --git a/Explorer/Assets/DCL/SDKComponents/MediaStream/MediaFactory.cs b/Explorer/Assets/DCL/SDKComponents/MediaStream/MediaFactory.cs index 0b107adff24..b87cdbef620 100644 --- a/Explorer/Assets/DCL/SDKComponents/MediaStream/MediaFactory.cs +++ b/Explorer/Assets/DCL/SDKComponents/MediaStream/MediaFactory.cs @@ -146,8 +146,10 @@ private MediaPlayerComponent CreateMediaPlayerComponent(string url, bool hasVolu if (url.IsLivekitAddress()) { +#if !UNITY_WEBGL isValidLocalPath = true; isValidStreamUrl = true; +#endif } else @@ -177,8 +179,10 @@ private MediaPlayerComponent CreateMediaPlayerComponent(string url, bool hasVolu { MultiMediaPlayer player = address.Match( (streamingRoom, mediaPlayerPool), - onUrlMediaAddress: static (ctx, address) => MultiMediaPlayer.FromAvProPlayer(new AvProPlayer(ctx.mediaPlayerPool.GetOrCreateReusableMediaPlayer(address.Url), ctx.mediaPlayerPool)), - onLivekitAddress: static (ctx, _) => MultiMediaPlayer.FromLivekitPlayer(new LivekitPlayer(ctx.streamingRoom)) + onUrlMediaAddress: static (ctx, address) => MultiMediaPlayer.FromAvProPlayer(new AvProPlayer(ctx.mediaPlayerPool.GetOrCreateReusableMediaPlayer(address.Url), ctx.mediaPlayerPool)) +#if !UNITY_WEBGL + , onLivekitAddress: static (ctx, _) => MultiMediaPlayer.FromLivekitPlayer(new LivekitPlayer(ctx.streamingRoom)) +#endif ); component = new MediaPlayerComponent(player, url.Contains(CONTENT_SERVER_PREFIX)) diff --git a/Explorer/Assets/DCL/SDKComponents/MediaStream/MediaPlayerExtensions.cs b/Explorer/Assets/DCL/SDKComponents/MediaStream/MediaPlayerExtensions.cs index 7b51e94054c..e412c6e76b1 100644 --- a/Explorer/Assets/DCL/SDKComponents/MediaStream/MediaPlayerExtensions.cs +++ b/Explorer/Assets/DCL/SDKComponents/MediaStream/MediaPlayerExtensions.cs @@ -45,6 +45,7 @@ public static void UpdatePlayback(this MediaPlayer mediaPlayer, bool hasPlaying, control.Stop(); } +#if !UNITY_WEBGL public static void UpdatePlayback(this LivekitPlayer mediaPlayer, bool hasPlaying, bool playing) { if (!mediaPlayer.MediaOpened) @@ -63,6 +64,7 @@ public static void UpdatePlayback(this LivekitPlayer mediaPlayer, bool hasPlayin else if (mediaPlayer.State is PlayerState.PLAYING) mediaPlayer.Stop(); } +#endif internal static UniTask SetPlaybackPropertiesAsync(IMediaControl control, PBVideoPlayer sdkVideoPlayer, bool isLiveStream = false) => SetPlaybackPropertiesAsync(control, diff --git a/Explorer/Assets/DCL/SDKComponents/MediaStream/MultiMediaPlayer.cs b/Explorer/Assets/DCL/SDKComponents/MediaStream/MultiMediaPlayer.cs index f2b9e057167..28a8a4b5fe3 100644 --- a/Explorer/Assets/DCL/SDKComponents/MediaStream/MultiMediaPlayer.cs +++ b/Explorer/Assets/DCL/SDKComponents/MediaStream/MultiMediaPlayer.cs @@ -37,77 +37,110 @@ public AvProPlayer(MediaPlayer avProMediaPlayer, MediaPlayerCustomPool mediaPlay [REnum] [REnumField(typeof(AvProPlayer))] +#if !UNITY_WEBGL [REnumField(typeof(LivekitPlayer))] +#endif public partial struct MultiMediaPlayer { public bool IsPlaying => Match( - static avPro => avPro.AvProMediaPlayer.Control.IsPlaying(), - static livekitPlayer => livekitPlayer.State is PlayerState.PLAYING + static avPro => avPro.AvProMediaPlayer.Control.IsPlaying() +#if !UNITY_WEBGL + , static livekitPlayer => livekitPlayer.State is PlayerState.PLAYING +#endif ); public float CurrentTime => Match( - static avProPlayer => (float)avProPlayer.AvProMediaPlayer.Control.GetCurrentTime(), - static _ => 0f + static avProPlayer => (float)avProPlayer.AvProMediaPlayer.Control.GetCurrentTime() +#if !UNITY_WEBGL + , static _ => 0f +#endif ); public float Duration => Match( - static avProPlayer => (float)avProPlayer.AvProMediaPlayer.Info.GetDuration(), - static _ => 0f + static avProPlayer => (float)avProPlayer.AvProMediaPlayer.Info.GetDuration() +#if !UNITY_WEBGL + , static _ => 0f +#endif ); public bool IsFinished => Match( - static avPro => avPro.AvProMediaPlayer.Control.IsFinished(), - static livekitPlayer => livekitPlayer.State is PlayerState.STOPPED + static avPro => avPro.AvProMediaPlayer.Control.IsFinished() +#if !UNITY_WEBGL + , static livekitPlayer => livekitPlayer.State is PlayerState.STOPPED +#endif ); public bool IsPaused => Match( - static avPro => avPro.AvProMediaPlayer.Control.IsPaused(), - static livekitPlayer => livekitPlayer.State is PlayerState.PAUSED + static avPro => avPro.AvProMediaPlayer.Control.IsPaused() +#if !UNITY_WEBGL + , static livekitPlayer => livekitPlayer.State is PlayerState.PAUSED +#endif ); public bool IsSeeking => Match( - static avPro => avPro.AvProMediaPlayer.Control.IsSeeking(), - static _ => false + static avPro => avPro.AvProMediaPlayer.Control.IsSeeking() +#if !UNITY_WEBGL + , static _ => false +#endif ); public bool IsBuffering => Match( - static avPro => avPro.AvProMediaPlayer.Control.IsBuffering(), - static _ => false + static avPro => avPro.AvProMediaPlayer.Control.IsBuffering() +#if !UNITY_WEBGL + , static _ => false +#endif ); public bool HasControl => Match( - static avPro => avPro.AvProMediaPlayer.Control != null, - static _ => false + static avPro => avPro.AvProMediaPlayer.Control != null +#if !UNITY_WEBGL + , static _ => false +#endif ); public bool IsReady => Match( - static avPro => avPro.AvProMediaPlayer.TextureProducer != null, - static _ => true + static avPro => avPro.AvProMediaPlayer.TextureProducer != null +#if !UNITY_WEBGL + , static _ => true +#endif ); public bool WaitingForProperties => Match( - static avPro => avPro.WaitingForProperties, - static _ => false + static avPro => avPro.WaitingForProperties +#if !UNITY_WEBGL + , static _ => false +#endif ); public Vector2 GetTexureScale => Match(static avPro => { float vScale = avPro.AvProMediaPlayer.TextureProducer.RequiresVerticalFlip() ? -1 : 1; return new Vector2(1, vScale); - }, - static _ => new Vector2(1, -1) + } +#if !UNITY_WEBGL + , static _ => new Vector2(1, -1) +#endif ); - public bool IsSpatial => Match(static avPro => Mathf.Approximately(avPro.AvProMediaPlayer.AudioSource?.spatialBlend ?? 0f, 1f), - static _ => false); + public bool IsSpatial => Match(static avPro => Mathf.Approximately(avPro.AvProMediaPlayer.AudioSource?.spatialBlend ?? 0f, 1f) +#if !UNITY_WEBGL + , static _ => false +#endif + ); public float SpatialMaxDistance => Match( - static avPro => avPro.AvProMediaPlayer.AudioSource?.maxDistance ?? MediaPlayerComponent.DEFAULT_SPATIAL_MAX_DISTANCE, - static _ => 0f); + static avPro => avPro.AvProMediaPlayer.AudioSource?.maxDistance ?? MediaPlayerComponent.DEFAULT_SPATIAL_MAX_DISTANCE +#if !UNITY_WEBGL + , static _ => 0f +#endif + ); public float SpatialMinDistance => Match( - static avPro => avPro.AvProMediaPlayer.AudioSource?.minDistance ?? MediaPlayerComponent.DEFAULT_SPATIAL_MIN_DISTANCE, - static _ => 0f); + static avPro => avPro.AvProMediaPlayer.AudioSource?.minDistance ?? MediaPlayerComponent.DEFAULT_SPATIAL_MIN_DISTANCE +#if !UNITY_WEBGL + , static _ => 0f +#endif + ); public void Dispose(MediaAddress address) { @@ -117,16 +150,20 @@ public void Dispose(MediaAddress address) { if (address.IsUrlMediaAddress(out var url)) avPro.MediaPlayerCustomPool.ReleaseMediaPlayer(url!.Url, avPro.AvProMediaPlayer); - }, - onLivekitPlayer: static (_, livekitPlayer) => livekitPlayer.Dispose() + } +#if !UNITY_WEBGL + , onLivekitPlayer: static (_, livekitPlayer) => livekitPlayer.Dispose() +#endif ); } public void CloseCurrentStream() { Match( - static avPro => avPro.AvProMediaPlayer.CloseCurrentStream(), - static livekitPlayer => livekitPlayer.CloseCurrentStream() + static avPro => avPro.AvProMediaPlayer.CloseCurrentStream() +#if !UNITY_WEBGL + , static livekitPlayer => livekitPlayer.CloseCurrentStream() +#endif ); } @@ -137,16 +174,20 @@ public void PlaceAt(Vector3 position) { Match( position, - static (pose, avPlayer) => avPlayer.AvProMediaPlayer.transform.position = pose, - static (pose, livekitPlayer) => livekitPlayer.PlaceAudioAt(pose) + static (pose, avPlayer) => avPlayer.AvProMediaPlayer.transform.position = pose +#if !UNITY_WEBGL + , static (pose, livekitPlayer) => livekitPlayer.PlaceAudioAt(pose) +#endif ); } public Texture? LastTexture() { return Match( - static avPro => avPro.AvProMediaPlayer.TextureProducer.GetTexture(), - static livekitPlayer => livekitPlayer.LastTexture() + static avPro => avPro.AvProMediaPlayer.TextureProducer.GetTexture() +#if !UNITY_WEBGL + , static livekitPlayer => livekitPlayer.LastTexture() +#endif ); } @@ -154,16 +195,22 @@ public void UpdateVolume(float volume) { Match( volume, - static (ctx, avPro) => avPro.AvProMediaPlayer.AudioVolume = ctx, - static (ctx, livekitPlayer) => livekitPlayer!.SetVolume(ctx)); + static (ctx, avPro) => avPro.AvProMediaPlayer.AudioVolume = ctx +#if !UNITY_WEBGL + , static (ctx, livekitPlayer) => livekitPlayer!.SetVolume(ctx) +#endif + ); } public readonly void CrossfadeVolume(float volume, float volumeDelta = 1) { Match( (volume, volumeDelta), - static (ctx, avPro) => avPro.AvProMediaPlayer.CrossfadeVolume(ctx.volume, ctx.volumeDelta), - static (ctx, livekitPlayer) => livekitPlayer!.CrossfadeVolume(ctx.volume, ctx.volumeDelta)); + static (ctx, avPro) => avPro.AvProMediaPlayer.CrossfadeVolume(ctx.volume, ctx.volumeDelta) +#if !UNITY_WEBGL + , static (ctx, livekitPlayer) => livekitPlayer!.CrossfadeVolume(ctx.volume, ctx.volumeDelta) +#endif + ); } public void UpdatePlaybackProperties(PBVideoPlayer sdkVideoPlayer) @@ -182,16 +229,21 @@ public void UpdatePlayback(bool hasPlaying, bool isPlaying) { Match( (hasPlaying, isPlaying), - static (ctx, avPro) => avPro.AvProMediaPlayer.UpdatePlayback(ctx.hasPlaying, ctx.isPlaying), - static (ctx, livekitPlayer) => livekitPlayer.UpdatePlayback(ctx.hasPlaying, ctx.isPlaying) + static (ctx, avPro) => avPro.AvProMediaPlayer.UpdatePlayback(ctx.hasPlaying, ctx.isPlaying) +#if !UNITY_WEBGL + , static (ctx, livekitPlayer) => livekitPlayer.UpdatePlayback(ctx.hasPlaying, ctx.isPlaying) +#endif ); } public readonly void SetLooping(bool isLooping) => Match( isLooping, - static (ctx, avPro) => avPro.AvProMediaPlayer.Control.SetLooping(ctx), - static (_, _) => { }); + static (ctx, avPro) => avPro.AvProMediaPlayer.Control.SetLooping(ctx) +#if !UNITY_WEBGL + , static (_, _) => { } +#endif + ); public readonly async UniTaskVoid SetPlaybackPropertiesAsync(PBVideoPlayer sdkVideoPlayer, bool isLiveStream = false) { @@ -238,13 +290,16 @@ public bool OpenMedia(MediaAddress mediaAddress, bool isFromContentServer, bool string target = ctx.isFromContentServer ? string.Format("{0}?includeMimeType", address.Url) : address.Url; return player.OpenMedia(MediaPathType.AbsolutePathOrURL, target, ctx.autoPlay); - }, + } +#if !UNITY_WEBGL + , onLivekitAddress: static (ctx, address) => { bool result = ctx.player.IsLivekitPlayer(out var livekitPlayer); livekitPlayer?.OpenMedia(address); return result; } +#endif ); } @@ -271,24 +326,30 @@ public void TrySeek(double seekTime) public void Play() { Match( - static avPro => avPro.AvProMediaPlayer.Control.Play(), - static livekitPlayer => livekitPlayer.Play() + static avPro => avPro.AvProMediaPlayer.Control.Play() +#if !UNITY_WEBGL + , static livekitPlayer => livekitPlayer.Play() +#endif ); } public void Pause() { Match( - static avPro => avPro.AvProMediaPlayer.Control.Pause(), - static livekitPlayer => livekitPlayer.Pause() + static avPro => avPro.AvProMediaPlayer.Control.Pause() +#if !UNITY_WEBGL + , static livekitPlayer => livekitPlayer.Pause() +#endif ); } public ErrorCode GetLastError() { return Match( - static avPro => avPro.AvProMediaPlayer.Control.GetLastError(), - static _ => ErrorCode.None + static avPro => avPro.AvProMediaPlayer.Control.GetLastError() +#if !UNITY_WEBGL + , static _ => ErrorCode.None +#endif ); } @@ -303,8 +364,11 @@ public void UpdateSpatialAudio(bool isSpatial, float minDistance, float maxDista audioSource.minDistance = args.minDistance; audioSource.maxDistance = args.maxDistance; audioSource.rolloffMode = AudioRolloffMode.Linear; - }, - static (_, _) => { }); + } +#if !UNITY_WEBGL + , static (_, _) => { } +#endif + ); } /// @@ -315,8 +379,10 @@ public void UpdateSpatialAudio(bool isSpatial, float minDistance, float maxDista public AudioSource? AnyExposedAudioSource() { return Match( - static avPro => avPro.AvProMediaPlayer.AudioSource, - static livekitPlayer => livekitPlayer.AnyExposedAudioSource() + static avPro => avPro.AvProMediaPlayer.AudioSource +#if !UNITY_WEBGL + , static livekitPlayer => livekitPlayer.AnyExposedAudioSource() +#endif ); } } diff --git a/Explorer/Assets/DCL/SDKComponents/MediaStream/Systems/CreateMediaPlayerSystem.cs b/Explorer/Assets/DCL/SDKComponents/MediaStream/Systems/CreateMediaPlayerSystem.cs index 4fba51f00c3..eaaf4be9354 100644 --- a/Explorer/Assets/DCL/SDKComponents/MediaStream/Systems/CreateMediaPlayerSystem.cs +++ b/Explorer/Assets/DCL/SDKComponents/MediaStream/Systems/CreateMediaPlayerSystem.cs @@ -56,9 +56,11 @@ private void CreateVideoPlayer(Entity entity, PBVideoPlayer sdkComponent) { var address = MediaAddress.New(sdkComponent.Src!); +#if !UNITY_WEBGL // Streams rely on livekit room being active; which can only be in we are on the same scene. Let's not create media that is wrong if (address.IsLivekitAddress(out _) && !sceneStateProvider.IsCurrent) return; +#endif // MediaPlayerComponent / VideoTextureConsumer can be present in any combination diff --git a/Explorer/Assets/DCL/SDKComponents/MediaStream/Systems/UpdateMediaPlayerSystem.cs b/Explorer/Assets/DCL/SDKComponents/MediaStream/Systems/UpdateMediaPlayerSystem.cs index 37e7987aaec..0d1de2dfd6e 100644 --- a/Explorer/Assets/DCL/SDKComponents/MediaStream/Systems/UpdateMediaPlayerSystem.cs +++ b/Explorer/Assets/DCL/SDKComponents/MediaStream/Systems/UpdateMediaPlayerSystem.cs @@ -96,9 +96,11 @@ private void UpdateAudioStream(in Entity entity, ref MediaPlayerComponent compon if (sdkComponent.HasPlaying && sdkComponent.Playing != component.IsPlaying) component.MediaPlayer.UpdatePlayback(sdkComponent.HasPlaying, sdkComponent.Playing); +#if !UNITY_WEBGL if (component.IsPlaying) if (component.MediaPlayer.IsLivekitPlayer(out LivekitPlayer? livekitPlayer)) livekitPlayer?.EnsureAudioIsPlaying(); +#endif bool hasSpatialEnabledChanged = sdkComponent.HasSpatial && sdkComponent.Spatial != component.IsSpatial; @@ -146,12 +148,14 @@ private void UpdateVideoStream(in Entity entity, ref MediaPlayerComponent compon component.MediaPlayer.UpdatePlaybackProperties(sdkComponent); } +#if !UNITY_WEBGL if (component.IsPlaying) // Covers cases like leaving and re-entering the scene // or the stream not being available for some time, like OBS not started while the stream is active if (component.MediaPlayer.IsLivekitPlayer(out LivekitPlayer? livekitPlayer)) livekitPlayer?.EnsureVideoIsPlaying(); +#endif bool hasSpatialEnabledChanged = sdkComponent.HasSpatial && sdkComponent.Spatial != component.IsSpatial; @@ -206,6 +210,7 @@ private void UpdateVideoTexture(ref MediaPlayerComponent playerComponent, ref Vi return; } +#if !UNITY_WEBGL if (playerComponent.MediaPlayer.IsLivekitPlayer(out LivekitPlayer livekitPlayer)) { if (!livekitPlayer.IsVideoOpened) @@ -214,6 +219,7 @@ private void UpdateVideoTexture(ref MediaPlayerComponent playerComponent, ref Vi return; } } +#endif // Video is already playing in the background, and CopyTexture is a GPU operation, // so it does not make sense to budget by CPU as it can lead to much worse UX @@ -224,6 +230,7 @@ private void UpdateVideoTexture(ref MediaPlayerComponent playerComponent, ref Vi int targetWidth = avText.width; int targetHeight = avText.height; +#if !UNITY_WEBGL // Cap LiveKit video resolution to prevent GPU stalls from 4K+ streams. if (livekitPlayer != null && (avText.width > MAX_LIVEKIT_VIDEO_WIDTH || avText.height > MAX_LIVEKIT_VIDEO_HEIGHT)) { @@ -231,6 +238,7 @@ private void UpdateVideoTexture(ref MediaPlayerComponent playerComponent, ref Vi targetWidth = Mathf.RoundToInt(avText.width * scale); targetHeight = Mathf.RoundToInt(avText.height * scale); } +#endif if (assignedTexture.Texture.width != targetWidth || assignedTexture.Texture.height != targetHeight) assignedTexture.Resize(targetWidth, targetHeight); @@ -267,7 +275,11 @@ private void ToggleCurrentStreamsState(Entity entity, MediaPlayerComponent media { if (enteredScene) return; +#if !UNITY_WEBGL bool isLivekit = mediaPlayerComponent.MediaPlayer.IsLivekitPlayer(out _); +#else + bool isLivekit = false; +#endif // Livekit streams rely on the livekit room being active for the current scene only. // Non-livekit streams are also stopped when PlayCurrentSceneStreamOnly is on so they can be diff --git a/Explorer/Assets/DCL/UserInAppInitializationFlow/RealUserInAppInitializationFlow.cs b/Explorer/Assets/DCL/UserInAppInitializationFlow/RealUserInAppInitializationFlow.cs index 4007361cadf..6fe3c28822b 100644 --- a/Explorer/Assets/DCL/UserInAppInitializationFlow/RealUserInAppInitializationFlow.cs +++ b/Explorer/Assets/DCL/UserInAppInitializationFlow/RealUserInAppInitializationFlow.cs @@ -105,8 +105,13 @@ public async UniTask ExecuteAsync(UserInAppInitializationFlowParameters paramete // Clear cached identity for non-first instances in local scene development // This ensures each instance (except the first one) shows the authentication screen if (!appArgs.HasFlagWithValueTrue(AppArgsFlags.SKIP_AUTH_SCREEN) && - appArgs.HasFlagWithValueTrue(AppArgsFlags.LOCAL_SCENE) && - FileDCLPlayerPrefs.PrefsInstanceNumber > 0) + appArgs.HasFlagWithValueTrue(AppArgsFlags.LOCAL_SCENE) + +#if !UNITY_WEBGL + && FileDCLPlayerPrefs.PrefsInstanceNumber > 0 +#endif + + ) { identityCache.Clear(); } diff --git a/Explorer/Assets/DCL/Web3/Authenticators/Implementations/TokenFileAuthenticator.cs b/Explorer/Assets/DCL/Web3/Authenticators/Implementations/TokenFileAuthenticator.cs index ea2d40aa707..535a1b74e83 100644 --- a/Explorer/Assets/DCL/Web3/Authenticators/Implementations/TokenFileAuthenticator.cs +++ b/Explorer/Assets/DCL/Web3/Authenticators/Implementations/TokenFileAuthenticator.cs @@ -59,6 +59,9 @@ public async UniTask LoginAsync(LoginPayload _, CancellationToken private async UniTask LoginAsync(CancellationToken ct) { +#if UNITY_WEBGL + throw new AutoLoginTokenNotFoundException(); +#else if (!File.Exists(TOKEN_PATH)) throw new AutoLoginTokenNotFoundException(); @@ -94,6 +97,7 @@ private async UniTask LoginAsync(CancellationToken ct) return new DecentralandIdentity(new Web3Address(address), ephemeralAccount, expiration, authChain, IWeb3Identity.Web3IdentitySource.TokenFile); +#endif } public UniTask LogoutAsync(CancellationToken ct) => diff --git a/Explorer/Assets/DCL/WebRequests/ChromeDevtool/ChromeDevToolHandler.cs b/Explorer/Assets/DCL/WebRequests/ChromeDevtool/ChromeDevToolHandler.cs index c6e8e7ea7d0..b1845f7c619 100644 --- a/Explorer/Assets/DCL/WebRequests/ChromeDevtool/ChromeDevToolHandler.cs +++ b/Explorer/Assets/DCL/WebRequests/ChromeDevtool/ChromeDevToolHandler.cs @@ -27,9 +27,7 @@ public class ChromeDevToolHandler : IWebRequestAnalyticsHandler, IDisposable private readonly Dictionary uwrToScope; private readonly List webRequestScopes; -#if !UNITY_WEBGL private readonly IBridge bridge; -#endif private readonly CancellationTokenSource cancellationTokenSource = new (); public ChromeDevToolHandler(int maxConcurrency, IBridge bridge) @@ -96,7 +94,11 @@ public static ChromeDevToolHandler New(bool startOnCreation, IAppArgs appArgs) #endif } +#if !UNITY_WEBGL public BridgeStatus Status => bridge.Status; +#else + public BridgeStatus Status => BridgeStatus.Offline; +#endif public BridgeStartResult StartAndOpen() { diff --git a/Explorer/Assets/Plugins/NativeAudioAnalysis/Playground/AudioAnalysisPlayground.cs b/Explorer/Assets/Plugins/NativeAudioAnalysis/Playground/AudioAnalysisPlayground.cs index 950ad828dae..6c09eee95bf 100644 --- a/Explorer/Assets/Plugins/NativeAudioAnalysis/Playground/AudioAnalysisPlayground.cs +++ b/Explorer/Assets/Plugins/NativeAudioAnalysis/Playground/AudioAnalysisPlayground.cs @@ -35,6 +35,8 @@ public class AudioAnalysisPlayground : MonoBehaviour [SerializeField] private float rotateByBpmSpeed = 10; +#if !UNITY_WEBGL + private Camera mainCamera = null!; private int sampleRate; @@ -81,5 +83,7 @@ private void Update() // Amplitude amplitudeCenter.localScale = Vector3.one * lastAnalysis.amplitude * amplitudePower; } + +#endif } } diff --git a/Explorer/Assets/Plugins/WebGLSockets/JsWebSocket.cs b/Explorer/Assets/Plugins/WebGLSockets/JsWebSocket.cs index f05dcc99b2a..d966020adf9 100644 --- a/Explorer/Assets/Plugins/WebGLSockets/JsWebSocket.cs +++ b/Explorer/Assets/Plugins/WebGLSockets/JsWebSocket.cs @@ -6,7 +6,6 @@ using Cysharp.Threading.Tasks; using Utility.Networking; using RichTypes; -using DCL.Diagnostics; namespace DCL.WebSockets.JS { @@ -167,7 +166,7 @@ public async UniTask ConnectAsync(Uri uri, CancellationToken cancellationToken) return; } // Connect failed (State=Closed/Aborted). Caller will typically dispose; use LogWarning so stack trace isn't mistaken for a crash. - ReportHub.LogWarning(ReportCategory.Unspecified, $"JsWebSocket.cs: Connect failed, handleId: {handleId}, message: {result.ErrorMessage}, uri: {uri}. Caller may dispose."); + // DCL.Diagnostics.ReportHub.LogWarning(ReportData.UNSPECIFIED, $"JsWebSocket.cs: Connect failed, handleId: {handleId}, message: {result.ErrorMessage}, uri: {uri}. Caller may dispose."); throw new WebSocketException(); } diff --git a/Explorer/Packages/manifest.json b/Explorer/Packages/manifest.json index 57333295e8b..45cd33ea8cb 100644 --- a/Explorer/Packages/manifest.json +++ b/Explorer/Packages/manifest.json @@ -4,7 +4,6 @@ "com.renderheads.avpro.video-ultra": "git@github.com:decentraland/unity-explorer-packages.git?path=/AVProVideo", "com.alttester.sdk": "2.3.0", "com.arch.systemgroups": "https://github.com/mikhail-dcl/Arch.SystemGroups.git?path=/Arch.SystemGroups", - "com.atteneder.draco": "4.0.2", "com.atteneder.gltfast": "https://github.com/decentraland/unity-gltf.git", "com.brunomikoski.animationsequencer": "0.5.4", "com.coffee.softmask-for-ugui": "https://github.com/mob-sakai/SoftMaskForUGUI.git?path=Packages/src#3.5.0", @@ -82,7 +81,6 @@ "scopes": [ "com.demigiant", "com.coffee.ui-particle", - "com.atteneder.draco", "com.madsbangh.easybuttons", "com.brunomikoski", "com.mackysoft", diff --git a/Explorer/Packages/packages-lock.json b/Explorer/Packages/packages-lock.json index 8d298fdc188..cde7a34554c 100644 --- a/Explorer/Packages/packages-lock.json +++ b/Explorer/Packages/packages-lock.json @@ -31,15 +31,6 @@ "dependencies": {}, "hash": "bd58275fb169d5076f0e7b12a4ee5b455c3fee56" }, - "com.atteneder.draco": { - "version": "4.0.2", - "depth": 0, - "source": "registry", - "dependencies": { - "com.unity.burst": "1.4.11" - }, - "url": "https://package.openupm.com" - }, "com.atteneder.gltfast": { "version": "https://github.com/decentraland/unity-gltf.git", "depth": 0, diff --git a/Explorer/ProjectSettings/ProjectSettings.asset b/Explorer/ProjectSettings/ProjectSettings.asset index ff555121947..a12957cfb80 100644 --- a/Explorer/ProjectSettings/ProjectSettings.asset +++ b/Explorer/ProjectSettings/ProjectSettings.asset @@ -932,7 +932,7 @@ PlayerSettings: ReservedCFE: CT_FB Standalone: UNITASK_DOTWEEN_SUPPORT;UNITY_PIPELINE_URP;DOTWEEN_ENABLED;VERBOSE_LOGS;CT_FB;CDP_EXCLUDE_PROCESS_BROWSER;URP_COMPATIBILITY_MODE;TMP_PRESENT;ALTTESTER VisionOS: CT_FB - WebGL: CT_FB + WebGL: UNITASK_DOTWEEN_SUPPORT;UNITY_PIPELINE_URP;DOTWEEN_ENABLED;VERBOSE_LOGS;CT_FB;CDP_EXCLUDE_PROCESS_BROWSER;URP_COMPATIBILITY_MODE;TMP_PRESENT;ALTTESTER Windows Store Apps: CT_FB XboxOne: CT_FB iPhone: CT_FB