< 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:56
Uncovered lines:36
Coverable lines:92
Total lines:262
Line coverage:60.8% (56 of 92)
Covered branches:0
Total branches:0

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%6.566075%
UnloadScene()0%3.333066.67%
SendCrdt()0%23.1216069.7%
GetCurrentState()0%1101000%

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 KernelCommunication;
 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
 27        private const string REQUIRED_PORT_ID_START = "scene-";
 28
 429        private int sceneNumber = -1;
 30        private RPCContext context;
 31        private RpcServerPort<RPCContext> port;
 32
 33        private readonly MemoryStream sendCrdtMemoryStream;
 34        private readonly BinaryWriter sendCrdtBinaryWriter;
 35        private readonly MemoryStream getStateMemoryStream;
 36        private readonly BinaryWriter getStateBinaryWriter;
 37
 438        private readonly CRDTSceneMessage reusableCrdtMessageResult = new CRDTSceneMessage();
 39
 40        public static void RegisterService(RpcServerPort<RPCContext> port)
 41        {
 342            if (!port.portName.StartsWith(REQUIRED_PORT_ID_START)) return;
 43
 144            RpcSceneControllerServiceCodeGen.RegisterService(port, new SceneControllerServiceImpl(port));
 145        }
 46
 447        public SceneControllerServiceImpl(RpcServerPort<RPCContext> port)
 48        {
 449            port.OnClose += OnPortClose;
 450            this.port = port;
 51
 452            sendCrdtMemoryStream = new MemoryStream();
 453            sendCrdtBinaryWriter = new BinaryWriter(sendCrdtMemoryStream);
 54
 455            getStateMemoryStream = new MemoryStream();
 456            getStateBinaryWriter = new BinaryWriter(getStateMemoryStream);
 457        }
 58
 59        private void OnPortClose()
 60        {
 461            port.OnClose -= OnPortClose;
 62
 463            if (context != null && context.crdt.WorldState.ContainsScene(sceneNumber))
 264                UnloadScene(null, context, new CancellationToken()).Forget();
 465        }
 66
 67        public async UniTask<LoadSceneResult> LoadScene(LoadSceneMessage request, RPCContext context, CancellationToken 
 68        {
 369            sceneNumber = request.SceneNumber;
 370            this.context = context;
 71
 372            List<ContentServerUtils.MappingPair> parsedContent = new List<ContentServerUtils.MappingPair>();
 73
 674            for (var i = 0; i < request.Entity.Content.Count; i++)
 75            {
 076                parsedContent.Add(new ContentServerUtils.MappingPair()
 77                {
 78                    file = request.Entity.Content[i].File,
 79                    hash = request.Entity.Content[i].Hash
 80                });
 81            }
 82
 383            CatalystSceneEntityMetadata parsedMetadata = Utils.SafeFromJson<CatalystSceneEntityMetadata>(request.Entity.
 384            Vector2Int[] parsedParcels = new Vector2Int[parsedMetadata.scene.parcels.Length];
 85
 1286            for (int i = 0; i < parsedMetadata.scene.parcels.Length; i++)
 87            {
 388                parsedParcels[i] = Utils.StringToVector2Int(parsedMetadata.scene.parcels[i]);
 89            }
 90
 391            await UniTask.SwitchToMainThread(ct);
 92
 393            if (request.IsGlobalScene)
 94            {
 095                CreateGlobalSceneMessage globalScene = new CreateGlobalSceneMessage()
 96                {
 97                    contents = parsedContent,
 98                    id = request.Entity.Id,
 99                    sdk7 = request.Sdk7,
 100                    name = request.SceneName,
 101                    baseUrl = request.BaseUrl,
 102                    sceneNumber = sceneNumber,
 103                    isPortableExperience = request.IsPortableExperience,
 104                    icon = string.Empty // TODO: add icon url!
 105                };
 106
 0107                context.crdt.SceneController.CreateGlobalScene(globalScene);
 108            }
 109            else
 110            {
 3111                LoadParcelScenesMessage.UnityParcelScene unityParcelScene = new LoadParcelScenesMessage.UnityParcelScene
 112                {
 113                    sceneNumber = sceneNumber,
 114                    id = request.Entity.Id,
 115                    sdk7 = request.Sdk7,
 116                    baseUrl = request.BaseUrl,
 117                    baseUrlBundles = request.BaseUrlAssetBundles,
 118                    basePosition = Utils.StringToVector2Int(parsedMetadata.scene.@base),
 119                    parcels = parsedParcels,
 120                    contents = parsedContent
 121                };
 122
 3123                context.crdt.SceneController.LoadUnityParcelScene(unityParcelScene);
 124            }
 125
 3126            LoadSceneResult result = new LoadSceneResult() { Success = true };
 3127            return result;
 3128        }
 129
 130        public async UniTask<UnloadSceneResult> UnloadScene(UnloadSceneMessage request, RPCContext context, Cancellation
 131        {
 3132            await UniTask.SwitchToMainThread(ct);
 3133            context.crdt.SceneController.UnloadParcelSceneExecute(sceneNumber);
 134
 3135            return defaultUnloadSceneResult;
 3136        }
 137
 138        public async UniTask<CRDTSceneMessage> SendCrdt(CRDTSceneMessage request, RPCContext context, CancellationToken 
 139        {
 1140            IParcelScene scene = null;
 1141            CRDTServiceContext crdtContext = context.crdt;
 142
 1143            await UniTask.SwitchToMainThread(ct);
 144
 145            // This line is to avoid a race condition because a CRDT message could be sent before the scene was loaded
 146            // more info: https://github.com/decentraland/sdk/issues/480#issuecomment-1331309908
 4147            await UniTask.WaitUntil(() => crdtContext.WorldState.TryGetScene(sceneNumber, out scene),
 148                cancellationToken: ct);
 149
 4150            await UniTask.WaitWhile(() => crdtContext.MessagingControllersManager.HasScenePendingMessages(sceneNumber),
 151                cancellationToken: ct);
 152
 153            try
 154            {
 1155                int incomingCrdtCount = 0;
 1156                reusableCrdtMessageResult.Payload = ByteString.Empty;
 157
 1158                using (var iterator = CRDTDeserializer.DeserializeBatch(request.Payload.Memory))
 159                {
 2160                    while (iterator.MoveNext())
 161                    {
 1162                        if (!(iterator.Current is CRDTMessage crdtMessage))
 163                            continue;
 164
 1165                        crdtContext.CrdtMessageReceived?.Invoke(sceneNumber, crdtMessage);
 1166                        incomingCrdtCount++;
 167                    }
 1168                }
 169
 1170                if (incomingCrdtCount > 0)
 171                {
 172                    // When sdk7 scene receive it first crdt we set `InitMessagesDone` since
 173                    // kernel won't be sending that message for those scenes
 1174                    if (scene.sceneData.sdk7 && !scene.IsInitMessageDone())
 175                    {
 0176                        crdtContext.SceneController.EnqueueSceneMessage(new QueuedSceneMessage_Scene()
 177                        {
 178                            sceneNumber = sceneNumber,
 179                            tag = "scene",
 180                            payload = new Protocol.SceneReady(),
 181                            method = MessagingTypes.INIT_DONE,
 182                            type = QueuedSceneMessage.Type.SCENE_MESSAGE
 183                        });
 184                    }
 185                }
 186
 1187                if (crdtContext.scenesOutgoingCrdts.TryGetValue(sceneNumber, out CRDTProtocol sceneCrdtState))
 188                {
 0189                    sendCrdtMemoryStream.SetLength(0);
 0190                    crdtContext.scenesOutgoingCrdts.Remove(sceneNumber);
 0191                    KernelBinaryMessageSerializer.Serialize(sendCrdtBinaryWriter, sceneCrdtState);
 0192                    sceneCrdtState.ClearOnUpdated();
 0193                    reusableCrdtMessageResult.Payload = ByteString.CopyFrom(sendCrdtMemoryStream.ToArray());
 194                }
 1195            }
 196            catch (Exception e)
 197            {
 0198                Debug.LogError(e);
 0199            }
 200
 1201            return reusableCrdtMessageResult;
 1202        }
 203
 204        public async UniTask<CRDTSceneCurrentState> GetCurrentState(GetCurrentStateMessage request, RPCContext context, 
 205        {
 0206            CRDTProtocol outgoingMessages = null;
 0207            CRDTProtocol sceneState = null;
 0208            CRDTServiceContext crdtContext = context.crdt;
 209
 210            // we wait until messages for scene are set
 0211            await UniTask.WaitUntil(() => crdtContext.scenesOutgoingCrdts.TryGetValue(sceneNumber, out outgoingMessages)
 212                cancellationToken: ct);
 213
 0214            await UniTask.SwitchToMainThread(ct);
 215
 0216            if (crdtContext.CrdtExecutors != null && crdtContext.CrdtExecutors.TryGetValue(sceneNumber, out ICRDTExecuto
 217            {
 0218                sceneState = executor.crdtProtocol;
 219            }
 220
 0221            CRDTSceneCurrentState result = new CRDTSceneCurrentState
 222            {
 223                HasOwnEntities = false
 224            };
 225
 226            try
 227            {
 0228                getStateMemoryStream.SetLength(0);
 229
 230                // serialize outgoing messages
 0231                crdtContext.scenesOutgoingCrdts.Remove(sceneNumber);
 0232                KernelBinaryMessageSerializer.Serialize(getStateBinaryWriter, outgoingMessages);
 0233                outgoingMessages.ClearOnUpdated();
 234
 235                // serialize scene state
 0236                if (sceneState != null)
 237                {
 0238                    var state = sceneState.GetState();
 239
 0240                    for (int i = 0; i < state.Count; i++)
 241                    {
 0242                        if (state[i].data != null)
 243                        {
 0244                            result.HasOwnEntities = true;
 0245                            break;
 246                        }
 247                    }
 248
 0249                    KernelBinaryMessageSerializer.Serialize(getStateBinaryWriter, sceneState);
 250                }
 251
 0252                result.Payload = ByteString.CopyFrom(getStateMemoryStream.ToArray());
 0253            }
 254            catch (Exception e)
 255            {
 0256                Debug.LogError(e);
 0257            }
 258
 0259            return result;
 0260        }
 261    }
 262}