| | 1 | | using System; |
| | 2 | | using System.Collections; |
| | 3 | | using System.Collections.Generic; |
| | 4 | | using System.Net.Sockets; |
| | 5 | | using DCL; |
| | 6 | | using UnityEngine; |
| | 7 | | using WebSocketSharp.Server; |
| | 8 | |
|
| | 9 | | public class WebSocketCommunication : IKernelCommunication |
| | 10 | | { |
| | 11 | | WebSocketServer ws; |
| | 12 | | private Coroutine updateCoroutine; |
| | 13 | | private bool requestStop = false; |
| | 14 | |
|
| 0 | 15 | | private Dictionary<string, GameObject> bridgeGameObjects = new Dictionary<string, GameObject>(); |
| | 16 | |
|
| 0 | 17 | | public Dictionary<string, string> messageTypeToBridgeName = new Dictionary<string, string>(); // Public to be able t |
| | 18 | |
|
| | 19 | | [System.NonSerialized] |
| 0 | 20 | | public static Queue<DCLWebSocketService.Message> queuedMessages = new Queue<DCLWebSocketService.Message>(); |
| | 21 | |
|
| | 22 | | [System.NonSerialized] |
| | 23 | | public static volatile bool queuedMessagesDirty; |
| | 24 | |
|
| 0 | 25 | | public bool isServerReady => ws.IsListening; |
| | 26 | |
|
| | 27 | | private string StartServer(int port, int maxPort, bool withSSL) |
| | 28 | | { |
| 0 | 29 | | if (port > maxPort) |
| | 30 | | { |
| 0 | 31 | | throw new SocketException((int)SocketError.AddressAlreadyInUse); |
| | 32 | | } |
| | 33 | | string wssServerUrl; |
| 0 | 34 | | string wssServiceId = "dcl"; |
| | 35 | | try |
| | 36 | | { |
| 0 | 37 | | if (withSSL) |
| | 38 | | { |
| 0 | 39 | | wssServerUrl = $"wss://localhost:{port}/"; |
| 0 | 40 | | ws = new WebSocketServer(wssServerUrl) |
| | 41 | | { |
| | 42 | | SslConfiguration = |
| | 43 | | { |
| | 44 | | ServerCertificate = CertificateUtils.CreateSelfSignedCert(), |
| | 45 | | ClientCertificateRequired = false, |
| | 46 | | CheckCertificateRevocation = false, |
| 0 | 47 | | ClientCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true |
| | 48 | | }, |
| | 49 | | KeepClean = false |
| | 50 | | }; |
| 0 | 51 | | } |
| | 52 | | else |
| | 53 | | { |
| 0 | 54 | | wssServerUrl = $"ws://localhost:{port}/"; |
| 0 | 55 | | ws = new WebSocketServer(wssServerUrl); |
| | 56 | | } |
| | 57 | |
|
| 0 | 58 | | ws.AddWebSocketService<DCLWebSocketService>("/" + wssServiceId); |
| 0 | 59 | | ws.Start(); |
| 0 | 60 | | } |
| 0 | 61 | | catch (InvalidOperationException e) |
| | 62 | | { |
| 0 | 63 | | ws.Stop(); |
| 0 | 64 | | if (withSSL) // Search for available ports only if we're using SSL |
| | 65 | | { |
| 0 | 66 | | SocketException se = (SocketException)e.InnerException; |
| 0 | 67 | | if (se is { SocketErrorCode: SocketError.AddressAlreadyInUse }) |
| | 68 | | { |
| 0 | 69 | | return StartServer(port + 1, maxPort, withSSL); |
| | 70 | | } |
| | 71 | | } |
| 0 | 72 | | throw new InvalidOperationException(e.Message, e.InnerException); |
| | 73 | | } |
| | 74 | |
|
| 0 | 75 | | string wssUrl = wssServerUrl + wssServiceId; |
| 0 | 76 | | return wssUrl; |
| 0 | 77 | | } |
| | 78 | |
|
| 0 | 79 | | public WebSocketCommunication(bool withSSL = false, int startPort = 5000, int endPort = 5100) |
| | 80 | | { |
| 0 | 81 | | InitMessageTypeToBridgeName(); |
| | 82 | |
|
| 0 | 83 | | DCL.DataStore.i.debugConfig.isWssDebugMode = true; |
| | 84 | |
|
| 0 | 85 | | string url = StartServer(startPort, endPort, withSSL); |
| | 86 | |
|
| 0 | 87 | | Debug.Log("WebSocket Server URL: " + url); |
| | 88 | |
|
| 0 | 89 | | DataStore.i.wsCommunication.url = url; |
| | 90 | |
|
| 0 | 91 | | DataStore.i.wsCommunication.communicationReady.Set(true); |
| | 92 | |
|
| 0 | 93 | | updateCoroutine = CoroutineStarter.Start(ProcessMessages()); |
| 0 | 94 | | } |
| | 95 | |
|
| | 96 | | private void InitMessageTypeToBridgeName() |
| | 97 | | { |
| | 98 | | // Please, use `Bridges` as a bridge name, avoid adding messages here. The system will use `Bridges` as the defa |
| 0 | 99 | | messageTypeToBridgeName["SetDebug"] = "Main"; |
| 0 | 100 | | messageTypeToBridgeName["SetSceneDebugPanel"] = "Main"; |
| 0 | 101 | | messageTypeToBridgeName["ShowFPSPanel"] = "Main"; |
| 0 | 102 | | messageTypeToBridgeName["HideFPSPanel"] = "Main"; |
| 0 | 103 | | messageTypeToBridgeName["SetEngineDebugPanel"] = "Main"; |
| 0 | 104 | | messageTypeToBridgeName["SendSceneMessage"] = "Main"; |
| 0 | 105 | | messageTypeToBridgeName["LoadParcelScenes"] = "Main"; |
| 0 | 106 | | messageTypeToBridgeName["UnloadScene"] = "Main"; |
| 0 | 107 | | messageTypeToBridgeName["Reset"] = "Main"; |
| 0 | 108 | | messageTypeToBridgeName["CreateGlobalScene"] = "Main"; |
| 0 | 109 | | messageTypeToBridgeName["BuilderReady"] = "Main"; |
| 0 | 110 | | messageTypeToBridgeName["UpdateParcelScenes"] = "Main"; |
| 0 | 111 | | messageTypeToBridgeName["LoadProfile"] = "Main"; |
| 0 | 112 | | messageTypeToBridgeName["AddUserProfileToCatalog"] = "Main"; |
| 0 | 113 | | messageTypeToBridgeName["AddUserProfilesToCatalog"] = "Main"; |
| 0 | 114 | | messageTypeToBridgeName["RemoveUserProfilesFromCatalog"] = "Main"; |
| 0 | 115 | | messageTypeToBridgeName["ActivateRendering"] = "Main"; |
| 0 | 116 | | messageTypeToBridgeName["DeactivateRendering"] = "Main"; |
| 0 | 117 | | messageTypeToBridgeName["ForceActivateRendering"] = "Main"; |
| 0 | 118 | | messageTypeToBridgeName["AddWearablesToCatalog"] = "Main"; |
| 0 | 119 | | messageTypeToBridgeName["WearablesRequestFailed"] = "Main"; |
| 0 | 120 | | messageTypeToBridgeName["RemoveWearablesFromCatalog"] = "Main"; |
| 0 | 121 | | messageTypeToBridgeName["ClearWearableCatalog"] = "Main"; |
| 0 | 122 | | messageTypeToBridgeName["InitializeFriends"] = "Main"; |
| 0 | 123 | | messageTypeToBridgeName["UpdateFriendshipStatus"] = "Main"; |
| 0 | 124 | | messageTypeToBridgeName["UpdateUserPresence"] = "Main"; |
| 0 | 125 | | messageTypeToBridgeName["FriendNotFound"] = "Main"; |
| 0 | 126 | | messageTypeToBridgeName["AddMessageToChatWindow"] = "Main"; |
| 0 | 127 | | messageTypeToBridgeName["UpdateMinimapSceneInformation"] = "Main"; |
| 0 | 128 | | messageTypeToBridgeName["UpdateHotScenesList"] = "Main"; |
| 0 | 129 | | messageTypeToBridgeName["SetRenderProfile"] = "Main"; |
| 0 | 130 | | messageTypeToBridgeName["CrashPayloadRequest"] = "Main"; |
| 0 | 131 | | messageTypeToBridgeName["SetDisableAssetBundles"] = "Main"; |
| 0 | 132 | | messageTypeToBridgeName["DumpRendererLockersInfo"] = "Main"; |
| 0 | 133 | | messageTypeToBridgeName["PublishSceneResult"] = "Main"; |
| 0 | 134 | | messageTypeToBridgeName["BuilderProjectInfo"] = "Main"; |
| 0 | 135 | | messageTypeToBridgeName["BuilderInWorldCatalogHeaders"] = "Main"; |
| 0 | 136 | | messageTypeToBridgeName["RequestedHeaders"] = "Main"; |
| 0 | 137 | | messageTypeToBridgeName["AddAssets"] = "Main"; |
| 0 | 138 | | messageTypeToBridgeName["RunPerformanceMeterTool"] = "Main"; |
| 0 | 139 | | messageTypeToBridgeName["InstantiateBotsAtWorldPos"] = "Main"; |
| 0 | 140 | | messageTypeToBridgeName["InstantiateBotsAtCoords"] = "Main"; |
| 0 | 141 | | messageTypeToBridgeName["StartBotsRandomizedMovement"] = "Main"; |
| 0 | 142 | | messageTypeToBridgeName["StopBotsMovement"] = "Main"; |
| 0 | 143 | | messageTypeToBridgeName["RemoveBot"] = "Main"; |
| 0 | 144 | | messageTypeToBridgeName["ClearBots"] = "Main"; |
| 0 | 145 | | messageTypeToBridgeName["ToggleSceneBoundingBoxes"] = "Main"; |
| 0 | 146 | | messageTypeToBridgeName["TogglePreviewMenu"] = "Main"; |
| | 147 | |
|
| 0 | 148 | | messageTypeToBridgeName["Teleport"] = "CharacterController"; |
| | 149 | |
|
| 0 | 150 | | messageTypeToBridgeName["SetRotation"] = "CameraController"; |
| | 151 | |
|
| 0 | 152 | | messageTypeToBridgeName["ShowNotificationFromJson"] = "HUDController"; |
| 0 | 153 | | messageTypeToBridgeName["ConfigureHUDElement"] = "HUDController"; |
| 0 | 154 | | messageTypeToBridgeName["ShowTermsOfServices"] = "HUDController"; |
| 0 | 155 | | messageTypeToBridgeName["RequestTeleport"] = "HUDController"; |
| 0 | 156 | | messageTypeToBridgeName["ShowAvatarEditorInSignUp"] = "HUDController"; |
| 0 | 157 | | messageTypeToBridgeName["SetUserTalking"] = "HUDController"; |
| 0 | 158 | | messageTypeToBridgeName["SetUsersMuted"] = "HUDController"; |
| 0 | 159 | | messageTypeToBridgeName["ShowWelcomeNotification"] = "HUDController"; |
| 0 | 160 | | messageTypeToBridgeName["UpdateBalanceOfMANA"] = "HUDController"; |
| 0 | 161 | | messageTypeToBridgeName["SetPlayerTalking"] = "HUDController"; |
| 0 | 162 | | messageTypeToBridgeName["SetVoiceChatEnabledByScene"] = "HUDController"; |
| 0 | 163 | | messageTypeToBridgeName["TriggerSelfUserExpression"] = "HUDController"; |
| 0 | 164 | | messageTypeToBridgeName["AirdroppingRequest"] = "HUDController"; |
| | 165 | |
|
| 0 | 166 | | messageTypeToBridgeName["GetMousePosition"] = "BuilderController"; |
| 0 | 167 | | messageTypeToBridgeName["SelectGizmo"] = "BuilderController"; |
| 0 | 168 | | messageTypeToBridgeName["ResetObject"] = "BuilderController"; |
| 0 | 169 | | messageTypeToBridgeName["ZoomDelta"] = "BuilderController"; |
| 0 | 170 | | messageTypeToBridgeName["SetPlayMode"] = "BuilderController"; |
| 0 | 171 | | messageTypeToBridgeName["TakeScreenshot"] = "BuilderController"; |
| 0 | 172 | | messageTypeToBridgeName["ResetBuilderScene"] = "BuilderController"; |
| 0 | 173 | | messageTypeToBridgeName["SetBuilderCameraPosition"] = "BuilderController"; |
| 0 | 174 | | messageTypeToBridgeName["SetBuilderCameraRotation"] = "BuilderController"; |
| 0 | 175 | | messageTypeToBridgeName["ResetBuilderCameraZoom"] = "BuilderController"; |
| 0 | 176 | | messageTypeToBridgeName["SetGridResolution"] = "BuilderController"; |
| 0 | 177 | | messageTypeToBridgeName["OnBuilderKeyDown"] = "BuilderController"; |
| 0 | 178 | | messageTypeToBridgeName["UnloadBuilderScene"] = "BuilderController"; |
| 0 | 179 | | messageTypeToBridgeName["SetSelectedEntities"] = "BuilderController"; |
| 0 | 180 | | messageTypeToBridgeName["GetCameraTargetBuilder"] = "BuilderController"; |
| 0 | 181 | | messageTypeToBridgeName["PreloadFile"] = "BuilderController"; |
| 0 | 182 | | messageTypeToBridgeName["SetBuilderConfiguration"] = "BuilderController"; |
| | 183 | |
|
| 0 | 184 | | messageTypeToBridgeName["SetTutorialEnabled"] = "TutorialController"; |
| 0 | 185 | | messageTypeToBridgeName["SetTutorialEnabledForUsersThatAlreadyDidTheTutorial"] = "TutorialController"; |
| 0 | 186 | | } |
| | 187 | |
|
| | 188 | | IEnumerator ProcessMessages() |
| | 189 | | { |
| 0 | 190 | | var hudControllerGO = GameObject.Find("HUDController"); |
| 0 | 191 | | var mainGO = GameObject.Find("Main"); |
| | 192 | |
|
| 0 | 193 | | while (!requestStop) |
| | 194 | | { |
| 0 | 195 | | lock (queuedMessages) |
| | 196 | | { |
| 0 | 197 | | if (queuedMessagesDirty) |
| | 198 | | { |
| 0 | 199 | | while (queuedMessages.Count > 0) |
| | 200 | | { |
| 0 | 201 | | DCLWebSocketService.Message msg = queuedMessages.Dequeue(); |
| | 202 | |
|
| 0 | 203 | | switch (msg.type) |
| | 204 | | { |
| | 205 | | // Add to this list the messages that are used a lot and you want better performance |
| | 206 | | case "SendSceneMessage": |
| 0 | 207 | | DCL.Environment.i.world.sceneController.SendSceneMessage(msg.payload); |
| 0 | 208 | | break; |
| | 209 | | case "Reset": |
| 0 | 210 | | DCL.Environment.i.world.sceneController.UnloadAllScenesQueued(); |
| 0 | 211 | | break; |
| | 212 | | case "SetVoiceChatEnabledByScene": |
| 0 | 213 | | if (int.TryParse(msg.payload, out int value)) // The payload should be `string`, this wi |
| | 214 | | { |
| 0 | 215 | | hudControllerGO.SendMessage(msg.type, value); |
| | 216 | | } |
| 0 | 217 | | break; |
| | 218 | | case "RunPerformanceMeterTool": |
| 0 | 219 | | if (float.TryParse(msg.payload, out float durationInSeconds)) // The payload should be ` |
| | 220 | | { |
| 0 | 221 | | mainGO.SendMessage(msg.type, durationInSeconds); |
| | 222 | | } |
| 0 | 223 | | break; |
| | 224 | | default: |
| 0 | 225 | | if (!messageTypeToBridgeName.TryGetValue(msg.type, out string bridgeName)) |
| | 226 | | { |
| 0 | 227 | | bridgeName = "Bridges"; // Default bridge |
| | 228 | | } |
| | 229 | |
|
| 0 | 230 | | if (bridgeGameObjects.TryGetValue(bridgeName, out GameObject bridgeObject) == false) |
| | 231 | | { |
| 0 | 232 | | bridgeObject = GameObject.Find(bridgeName); |
| 0 | 233 | | bridgeGameObjects.Add(bridgeName, bridgeObject); |
| | 234 | | } |
| | 235 | |
|
| 0 | 236 | | if (bridgeObject != null) |
| | 237 | | { |
| 0 | 238 | | bridgeObject.SendMessage(msg.type, msg.payload); |
| | 239 | | } |
| | 240 | | break; |
| | 241 | | } |
| | 242 | |
|
| 0 | 243 | | if (DCLWebSocketService.VERBOSE) |
| | 244 | | { |
| 0 | 245 | | Debug.Log( |
| | 246 | | "<b><color=#0000FF>WebSocketCommunication</color></b> >>> Got it! passing message of typ |
| | 247 | | msg.type); |
| | 248 | | } |
| | 249 | | } |
| | 250 | | } |
| 0 | 251 | | } |
| 0 | 252 | | yield return null; |
| | 253 | | } |
| 0 | 254 | | } |
| 0 | 255 | | public void Dispose() { ws.Stop(); } |
| | 256 | | } |