< Summary

Class:RPC.Services.SceneControllerServiceImpl
Assembly:RPC.Services.SceneControllerService
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/WorldRuntime/KernelCommunication/RPC/Services/SceneControllerService/SceneControllerServiceImpl.cs
Covered lines:95
Uncovered lines:53
Coverable lines:148
Total lines:407
Line coverage:64.1% (95 of 148)
Covered branches:0
Total branches:0
Covered methods:10
Total methods:11
Method coverage:90.9% (10 of 11)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
SceneControllerServiceImpl()0%110100%
SceneControllerServiceImpl(...)0%110100%
RegisterService(...)0%220100%
OnPortClose()0%330100%
LoadScene()0%26.0915063.33%
UnloadScene()0%3.333066.67%
SendCrdt()0%54.1731071.11%
GetCurrentState()0%7.297081.82%
SendBatch()0%20400%
SendSceneMessages(...)0%254.218010%
ToScenePortableExperienceFeatureToggle(...)0%5.264057.14%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/WorldRuntime/KernelCommunication/RPC/Services/SceneControllerService/SceneControllerServiceImpl.cs

#LineLine coverage
 1using Cysharp.Threading.Tasks;
 2using DCL;
 3using DCL.Controllers;
 4using DCL.CRDT;
 5using DCL.Helpers;
 6using DCL.Models;
 7using Decentraland.Renderer.RendererServices;
 8using Google.Protobuf;
 9using MainScripts.DCL.Components;
 10using rpc_csharp;
 11using RPC.Context;
 12using System;
 13using System.Collections.Generic;
 14using System.IO;
 15using System.Threading;
 16using UnityEngine;
 17using BinaryWriter = KernelCommunication.BinaryWriter;
 18
 19namespace RPC.Services
 20{
 21    public class SceneControllerServiceImpl : IRpcSceneControllerService<RPCContext>
 22    {
 23        // HACK: Until we fix the code generator, we must replace all 'Decentraland.Common.Entity' for 'DCL.ECSComponent
 24        // to be able to access request.Entity properties.
 125        private static readonly UnloadSceneResult defaultUnloadSceneResult = new UnloadSceneResult();
 26
 127        private static readonly SendBatchResponse defaultSendBatchResult = new SendBatchResponse();
 28
 29        private const string REQUIRED_PORT_ID_START = "scene-";
 30
 731        private int sceneNumber = -1;
 32        private RPCContext context;
 33        private RpcServerPort<RPCContext> port;
 734        private bool isFirstMessage = true;
 35        private int receivedSendCrdtCalls = 0;
 36
 37        private readonly MemoryStream sendCrdtMemoryStream;
 38        private readonly BinaryWriter sendCrdtBinaryWriter;
 39        private readonly MemoryStream getStateMemoryStream;
 40        private readonly BinaryWriter getStateBinaryWriter;
 41
 42        private readonly MemoryStream serializeComponentBuffer;
 43        private readonly CodedOutputStream serializeComponentStream;
 44
 745        private readonly CRDTSceneMessage reusableCrdtMessageResult = new CRDTSceneMessage();
 46
 47        public static void RegisterService(RpcServerPort<RPCContext> port)
 48        {
 349            if (!port.portName.StartsWith(REQUIRED_PORT_ID_START)) return;
 50
 151            RpcSceneControllerServiceCodeGen.RegisterService(port, new SceneControllerServiceImpl(port));
 152        }
 53
 754        public SceneControllerServiceImpl(RpcServerPort<RPCContext> port)
 55        {
 756            port.OnClose += OnPortClose;
 757            this.port = port;
 58
 759            sendCrdtMemoryStream = new MemoryStream();
 760            sendCrdtBinaryWriter = new BinaryWriter(sendCrdtMemoryStream);
 61
 762            getStateMemoryStream = new MemoryStream();
 763            getStateBinaryWriter = new BinaryWriter(getStateMemoryStream);
 64
 765            serializeComponentBuffer = new MemoryStream();
 766            serializeComponentStream = new CodedOutputStream(serializeComponentBuffer);
 767        }
 68
 69        private void OnPortClose()
 70        {
 771            port.OnClose -= OnPortClose;
 72
 773            if (context != null && context.crdt.WorldState.ContainsScene(sceneNumber))
 574                UnloadScene(null, context, new CancellationToken()).Forget();
 775        }
 76
 77        public async UniTask<LoadSceneResult> LoadScene(LoadSceneMessage request, RPCContext context, CancellationToken 
 78        {
 679            sceneNumber = request.SceneNumber;
 680            this.context = context;
 81
 682            List<ContentServerUtils.MappingPair> parsedContent = new List<ContentServerUtils.MappingPair>();
 83
 1284            for (var i = 0; i < request.Entity.Content.Count; i++)
 85            {
 086                parsedContent.Add(new ContentServerUtils.MappingPair()
 87                {
 88                    file = request.Entity.Content[i].File,
 89                    hash = request.Entity.Content[i].Hash
 90                });
 91            }
 92
 693            CatalystSceneEntityMetadata parsedMetadata = Utils.SafeFromJson<CatalystSceneEntityMetadata>(request.Entity.
 694            Vector2Int[] parsedParcels = new Vector2Int[parsedMetadata.scene.parcels.Length];
 95
 2496            for (int i = 0; i < parsedMetadata.scene.parcels.Length; i++)
 97            {
 698                parsedParcels[i] = Utils.StringToVector2Int(parsedMetadata.scene.parcels[i]);
 99            }
 100
 6101            await UniTask.SwitchToMainThread(ct);
 102
 6103            if (request.IsGlobalScene)
 104            {
 0105                CreateGlobalSceneMessage globalScene = new CreateGlobalSceneMessage()
 106                {
 107                    contents = parsedContent,
 108                    id = request.Entity.Id,
 109                    sdk7 = request.Sdk7,
 110                    name = parsedMetadata.display?.title ?? request.SceneName,
 111                    baseUrl = request.BaseUrl,
 112                    sceneNumber = sceneNumber,
 113                    isPortableExperience = request.IsPortableExperience,
 114                    requiredPermissions = parsedMetadata.requiredPermissions,
 115                    allowedMediaHostnames = parsedMetadata.allowedMediaHostnames,
 116                    icon = parsedMetadata.display?.navmapThumbnail,
 117                    description = parsedMetadata.display?.description,
 118                };
 119
 120                try
 121                {
 0122                    context.crdt.SceneController.CreateGlobalScene(globalScene);
 0123                }
 124                catch (Exception e)
 125                {
 0126                    Debug.LogException(e);
 0127                }
 128            }
 129            else
 130            {
 6131                LoadParcelScenesMessage.UnityParcelScene unityParcelScene = new LoadParcelScenesMessage.UnityParcelScene
 132                {
 133                    sceneNumber = sceneNumber,
 134                    id = request.Entity.Id,
 135                    sdk7 = request.Sdk7,
 136                    baseUrl = request.BaseUrl,
 137                    baseUrlBundles = request.BaseUrlAssetBundles,
 138                    basePosition = Utils.StringToVector2Int(parsedMetadata.scene.@base),
 139                    parcels = parsedParcels,
 140                    contents = parsedContent,
 141                    requiredPermissions = parsedMetadata.requiredPermissions,
 142                    allowedMediaHostnames = parsedMetadata.allowedMediaHostnames,
 143                    scenePortableExperienceFeatureToggles = ToScenePortableExperienceFeatureToggle(parsedMetadata.featur
 144                };
 145
 146                try
 147                {
 6148                    context.crdt.SceneController.LoadUnityParcelScene(unityParcelScene);
 6149                }
 150                catch (Exception e)
 151                {
 0152                    Debug.LogException(e);
 0153                }
 154            }
 155
 6156            LoadSceneResult result = new LoadSceneResult() { Success = true };
 6157            return result;
 6158        }
 159
 160        public async UniTask<UnloadSceneResult> UnloadScene(UnloadSceneMessage request, RPCContext context, Cancellation
 161        {
 6162            await UniTask.SwitchToMainThread(ct);
 6163            context.crdt.SceneController.UnloadParcelSceneExecute(sceneNumber);
 164
 6165            return defaultUnloadSceneResult;
 6166        }
 167
 168        public async UniTask<CRDTSceneMessage> SendCrdt(CRDTSceneMessage request, RPCContext context, CancellationToken 
 169        {
 2170            IParcelScene scene = null;
 2171            CRDTServiceContext crdtContext = context.crdt;
 172
 2173            await UniTask.SwitchToMainThread(ct);
 174
 175            // This line is to avoid a race condition because a CRDT message could be sent before the scene was loaded
 176            // more info: https://github.com/decentraland/sdk/issues/480#issuecomment-1331309908
 2177            while (!crdtContext.WorldState.TryGetScene(sceneNumber, out scene))
 178            {
 0179                await UniTask.Yield(ct);
 180            }
 181
 2182            bool checkPendingMessages = !scene.sceneData.sdk7
 183                                        || (scene.sceneData.sdk7 && !scene.IsInitMessageDone());
 184
 2185            while (checkPendingMessages && crdtContext.MessagingControllersManager.HasScenePendingMessages(sceneNumber))
 186            {
 0187                await UniTask.Yield(ct);
 188            }
 189
 2190            reusableCrdtMessageResult.Payload = ByteString.Empty;
 2191            if (!scene.sceneData.sdk7) return reusableCrdtMessageResult;
 192
 2193            if (isFirstMessage && (!request.Payload.IsEmpty || receivedSendCrdtCalls > 0))
 2194                isFirstMessage = false;
 195
 2196            receivedSendCrdtCalls++;
 197
 198            try
 199            {
 2200                if (!isFirstMessage)
 201                {
 2202                    using (var iterator = CRDTDeserializer.DeserializeBatch(request.Payload.Memory))
 203                    {
 4204                        while (iterator.MoveNext())
 205                        {
 2206                            if (!(iterator.Current is CrdtMessage crdtMessage))
 207                                continue;
 208
 2209                            crdtContext.CrdtMessageReceived?.Invoke(sceneNumber, crdtMessage);
 210                        }
 2211                    }
 212
 2213                    if (crdtContext.GetSceneTick(sceneNumber) == 0)
 214                    {
 215                        // pause scene update until GLTFs are loaded
 3216                        while (!crdtContext.IsSceneGltfLoadingFinished(scene.sceneData.sceneNumber))
 217                        {
 6218                            await UniTask.Yield(ct);
 219                        }
 220
 221                        // When sdk7 scene receive it first crdt we set `InitMessagesDone` since
 222                        // kernel won't be sending that message for those scenes
 1223                        if (scene.sceneData.sdk7 && !scene.IsInitMessageDone())
 224                        {
 1225                            crdtContext.SceneController.EnqueueSceneMessage(new QueuedSceneMessage_Scene()
 226                            {
 227                                sceneNumber = sceneNumber,
 228                                tag = "scene",
 229                                payload = new Protocol.SceneReady(),
 230                                method = MessagingTypes.INIT_DONE,
 231                                type = QueuedSceneMessage.Type.SCENE_MESSAGE
 232                            });
 233                        }
 234                    }
 235                }
 236
 2237                sendCrdtMemoryStream.SetLength(0);
 238
 2239                SendSceneMessages(crdtContext, sceneNumber, sendCrdtBinaryWriter, serializeComponentBuffer, serializeCom
 240
 2241                reusableCrdtMessageResult.Payload = ByteString.CopyFrom(sendCrdtMemoryStream.ToArray());
 242
 2243                if (!isFirstMessage)
 2244                    crdtContext.IncreaseSceneTick(sceneNumber);
 2245            }
 0246            catch (OperationCanceledException _)
 247            {
 248                // Ignored
 0249            }
 250            catch (Exception e)
 251            {
 0252                Debug.LogError(e);
 0253            }
 254
 2255            return reusableCrdtMessageResult;
 2256        }
 257
 258        public async UniTask<CRDTSceneCurrentState> GetCurrentState(GetCurrentStateMessage request, RPCContext context, 
 259        {
 2260            CRDTProtocol sceneState = null;
 2261            CRDTServiceContext crdtContext = context.crdt;
 262
 2263            await UniTask.SwitchToMainThread(ct);
 264
 2265            if (crdtContext.CrdtExecutors != null && crdtContext.CrdtExecutors.TryGetValue(sceneNumber, out ICRDTExecuto
 266            {
 2267                sceneState = executor.crdtProtocol;
 268            }
 269
 2270            CRDTSceneCurrentState result = new CRDTSceneCurrentState
 271            {
 272                HasOwnEntities = false
 273            };
 274
 275            try
 276            {
 2277                getStateMemoryStream.SetLength(0);
 278
 2279                SendSceneMessages(crdtContext, sceneNumber, sendCrdtBinaryWriter, serializeComponentBuffer, serializeCom
 280
 281                // serialize scene state
 2282                if (sceneState != null)
 283                {
 2284                    var crdtMessages = sceneState.GetStateAsMessages();
 285
 8286                    for (int i = 0; i < crdtMessages.Count; i++)
 287                    {
 2288                        CRDTSerializer.Serialize(getStateBinaryWriter, crdtMessages[i]);
 289                    }
 290                }
 291
 2292                result.Payload = ByteString.CopyFrom(getStateMemoryStream.ToArray());
 2293            }
 294            catch (Exception e)
 295            {
 0296                Debug.LogError(e);
 0297            }
 298
 2299            return result;
 2300        }
 301
 302        public async UniTask<SendBatchResponse> SendBatch(SendBatchRequest request, RPCContext context, CancellationToke
 303        {
 0304            await UniTask.SwitchToMainThread(ct);
 305
 306            try
 307            {
 0308                RendererManyEntityActions sceneRequest = RendererManyEntityActions.Parser.ParseFrom(request.Payload);
 309
 0310                for (var i = 0; i < sceneRequest.Actions.Count; i++)
 311                {
 0312                    context.crdt.SceneController.EnqueueSceneMessage(
 313                        SDK6DataMapExtensions.SceneMessageFromSdk6Message(sceneRequest.Actions[i], sceneNumber)
 314                    );
 315                }
 0316            }
 317            catch (Exception e)
 318            {
 0319                Debug.LogError(e);
 0320            }
 321
 0322            return defaultSendBatchResult;
 0323        }
 324
 325        private static void SendSceneMessages(CRDTServiceContext crdtContext,
 326            int sceneNumber,
 327            BinaryWriter sendCrdtBinaryWriter,
 328            MemoryStream serializeComponentBuffer,
 329            CodedOutputStream serializeComponentStream,
 330            bool clearMessages)
 331        {
 4332            if (!crdtContext.CrdtExecutors.TryGetValue(sceneNumber, out ICRDTExecutor executor))
 0333                return;
 334
 4335            if (crdtContext.ScenesOutgoingMsgs.TryGetValue(sceneNumber, out var msgs))
 336            {
 0337                var pairs = msgs.Pairs;
 338
 0339                for (int i = 0; i < pairs.Count; i++)
 340                {
 0341                    var msg = pairs[i].value;
 0342                    int entityId = (int)pairs[i].key1;
 0343                    int componentId = pairs[i].key2;
 344
 0345                    if (msg.MessageType != CrdtMessageType.APPEND_COMPONENT
 346                        && msg.MessageType != CrdtMessageType.PUT_COMPONENT
 347                        && msg.MessageType != CrdtMessageType.DELETE_COMPONENT)
 348                    {
 0349                        if (clearMessages)
 0350                            msg.Dispose();
 351
 0352                        continue;
 353                    }
 354
 0355                    CRDTProtocol crdtProtocol = executor.crdtProtocol;
 356                    CrdtMessage crdtMessage;
 357
 0358                    if (msg.MessageType == CrdtMessageType.APPEND_COMPONENT || msg.MessageType == CrdtMessageType.PUT_CO
 0359                        msg.PooledWrappedComponent.WrappedComponentBase.SerializeTo(serializeComponentBuffer, serializeC
 360
 0361                    if (msg.MessageType == CrdtMessageType.APPEND_COMPONENT)
 362                    {
 0363                        crdtMessage = crdtProtocol.CreateSetMessage(entityId, componentId, serializeComponentBuffer.ToAr
 364                    }
 0365                    else if (msg.MessageType == CrdtMessageType.PUT_COMPONENT)
 366                    {
 0367                        crdtMessage = crdtProtocol.CreateLwwMessage(entityId, componentId, serializeComponentBuffer.ToAr
 368                    }
 369                    else
 370                    {
 0371                        crdtMessage = crdtProtocol.CreateLwwMessage(entityId, componentId, null);
 372                    }
 373
 0374                    CRDTProtocol.ProcessMessageResultType resultType = crdtProtocol.ProcessMessage(crdtMessage);
 375
 0376                    if (resultType == CRDTProtocol.ProcessMessageResultType.StateUpdatedData ||
 377                        resultType == CRDTProtocol.ProcessMessageResultType.StateUpdatedTimestamp ||
 378                        resultType == CRDTProtocol.ProcessMessageResultType.EntityWasDeleted ||
 379                        resultType == CRDTProtocol.ProcessMessageResultType.StateElementAddedToSet)
 380                    {
 0381                        CRDTSerializer.Serialize(sendCrdtBinaryWriter, crdtMessage);
 382                    }
 383
 0384                    if (clearMessages)
 0385                        msg.Dispose();
 386                }
 387
 0388                if (clearMessages)
 0389                    msgs.Clear();
 390            }
 4391        }
 392
 393        private ScenePortableExperienceFeatureToggles ToScenePortableExperienceFeatureToggle(string str)
 394        {
 6395            if (string.Compare(str, "enabled", StringComparison.OrdinalIgnoreCase) == 0)
 0396                return ScenePortableExperienceFeatureToggles.Enable;
 397
 6398            if (string.Compare(str, "disabled", StringComparison.OrdinalIgnoreCase) == 0)
 0399                return ScenePortableExperienceFeatureToggles.Disable;
 400
 6401            if (string.Compare(str, "hideui", StringComparison.OrdinalIgnoreCase) == 0)
 0402                return ScenePortableExperienceFeatureToggles.HideUi;
 403
 6404            return ScenePortableExperienceFeatureToggles.Enable;
 405        }
 406    }
 407}