/// <inheritdoc />
        public async Task <HubOnConnectionState> OnConnected([JetBrains.Annotations.NotNull] Hub hubConnectedTo)
        {
            if (hubConnectedTo == null)
            {
                throw new ArgumentNullException(nameof(hubConnectedTo));
            }

            //We should never be here unless auth worked
            //so we can assume that and just try to request character session data
            //for the account.
            CharacterSessionDataResponse characterSessionDataResponse = await SocialToGameClient.GetCharacterSessionDataByAccount(ClaimsReader.GetUserIdInt(hubConnectedTo.Context.User))
                                                                        .ConfigureAwait(false);

            //TODO: To support website chat we shouldn't disconnect just because they don't have a zone session.
            //If the session data request fails we should just abort
            //and disconnect, the user shouldn't be connecting
            if (!characterSessionDataResponse.isSuccessful)
            {
                if (Logger.IsEnabled(LogLevel.Warning))
                {
                    Logger.LogWarning($"Failed to Query SessionData for AccountId: {ClaimsReader.GetUserId(hubConnectedTo.Context.User)} Reason: {characterSessionDataResponse.ResultCode}");
                }

                //TODO: Eventually we don't want to do this.
                return(HubOnConnectionState.Abort);
            }

            //This is ABSOLUTELY CRITICAL we need to validate that the character header they sent actually
            //is the character they have a session as
            //NOT CHECKING THIS IS EQUIVALENT TO LETTING USERS PRETEND THEY ARE ANYONE!
            if (hubConnectedTo.Context.UserIdentifier != characterSessionDataResponse.CharacterId.ToString())
            {
                //We can log account name and id here, because they were successfully authed.
                if (Logger.IsEnabled(LogLevel.Warning))
                {
                    Logger.LogWarning($"User with AccountId: {ClaimsReader.GetUserName(hubConnectedTo.Context.User)}:{ClaimsReader.GetUserId(hubConnectedTo.Context.User)} attempted to spoof as CharacterId: {hubConnectedTo.Context.UserIdentifier} but had session for CharacterID: {characterSessionDataResponse.CharacterId}.");
                }

                return(HubOnConnectionState.Abort);
            }

            if (Logger.IsEnabled(LogLevel.Information))
            {
                Logger.LogInformation($"Recieved SessionData: Id: {characterSessionDataResponse.CharacterId} ZoneId: {characterSessionDataResponse.ZoneId}");
            }

            //Registers for lookup so that we can tell where a connection is zone-wise.
            ZoneLookupService.Register(hubConnectedTo.Context.ConnectionId, characterSessionDataResponse.ZoneId);

            //TODO: We should have group name builders. Not hardcoded
            //Join the zoneserver's chat channel group
            await hubConnectedTo.Groups.AddToGroupAsync(hubConnectedTo.Context.ConnectionId, $"zone:{characterSessionDataResponse.ZoneId}", hubConnectedTo.Context.ConnectionAborted)
            .ConfigureAwait(false);

            return(HubOnConnectionState.Success);
        }
Пример #2
0
        //TODO: We need to handle failure cases, maybe with a window popup and bringing back to the titlescreen.
        /// <inheritdoc />
        public async Task OnGameInitialized()
        {
            //When we reach this scene, the pre lobby burst scene
            //we need to actually connect to the zone/lobby.
            //it verry well could be a zone. Maybe we were in a party and are reconnecting to it
            //no matter what though, we need to get information about our
            //character session and then the zone it should be connecting to
            //then we can connect.

            //First we need to know what zone this session should be going to
            CharacterSessionDataResponse sessionData = await CharacterDataService.GetCharacterSessionData(CharacterDataRepo.CharacterId, AuthTokenRepo.RetrieveWithType())
                                                       .ConfigureAwait(true);

            //TODO: Handle this better
            if (!sessionData.isSuccessful)
            {
                Logger.Error($"Failed to query session data for Character: {CharacterDataRepo.CharacterId}. Cannot connect to instance server.");
                return;
            }

            ResolveServiceEndpointResponse zoneEndpointResponse = await ZoneService.GetServerEndpoint(sessionData.ZoneId);

            if (!zoneEndpointResponse.isSuccessful)
            {
                Logger.Error($"Failed to query endpoint for Zone: {sessionData.ZoneId} which Character: {CharacterDataRepo.CharacterId} is in. Cannot connect to instance server.");
                return;
            }

            //TODO: Don't hardcode gameserver connection details
            //As soon as we start we should attempt to connect to the login server.
            bool result = await Client.ConnectAsync(IPAddress.Parse(zoneEndpointResponse.Endpoint.EndpointAddress), zoneEndpointResponse.Endpoint.EndpointPort)
                          .ConfigureAwait(true);

            if (!result)
            {
                throw new InvalidOperationException($"Failed to connect to Server: {zoneEndpointResponse.Endpoint.EndpointAddress} Port: {zoneEndpointResponse.Endpoint.EndpointPort}");
            }

            if (Logger.IsDebugEnabled)
            {
                Logger.Debug($"Connected client. isConnected: {Client.isConnected}");
            }

            //Basically we just take the network client and tell the client manager to start dealing with it
            //since it's connecting the manager should start pumping the messages out of it.
            await NetworkClientManager.StartHandlingNetworkClient(Client)
            .ConfigureAwait(true);

            //We should broadcast that the connection has been established to any interested subscribers
            OnNetworkConnectionEstablished?.Invoke(this, EventArgs.Empty);
        }
Пример #3
0
        //TODO: Refactor this behind its own object to provide download URL for character.
        /// <inheritdoc />
        public async Task OnGameInitialized()
        {
            if (Logger.IsInfoEnabled)
            {
                Logger.Info("About to start downloading map data.");
            }

            //When we start the loading screen for the game
            //To know what world we should load we should
            CharacterSessionDataResponse characterSessionData = await CharacterService.GetCharacterSessionData(LocalCharacterData.CharacterId, AuthTokenRepo.RetrieveWithType())
                                                                .ConfigureAwait(false);

            if (!characterSessionData.isSuccessful)
            {
                Logger.Error($"Failed to query Character Session Data: {characterSessionData.ResultCode}:{(int)characterSessionData.ResultCode}");
                return;
            }

            //TODO: Handle failure
            ProjectVersionStage.AssertAlpha();
            //TODO: Handle throwing/error
            //We need to know the world the zone is it, so we can request a download URL for it.
            long worldId = await ZoneDataService.GetZoneWorld(characterSessionData.ZoneId)
                           .ConfigureAwait(false);

            //With the worldid we can get the download URL.
            ContentDownloadURLResponse urlDownloadResponse = await ContentService.RequestWorldDownloadUrl(worldId, AuthTokenRepo.RetrieveWithType())
                                                             .ConfigureAwait(false);

            //TODO: Handle failure
            if (urlDownloadResponse.isSuccessful)
            {
                if (Logger.IsInfoEnabled)
                {
                    Logger.Info($"Download URL: {urlDownloadResponse.DownloadURL}");
                }

                //Can't do web request not on the main thread, sadly.
                await new UnityYieldAwaitable();

                //TODO: Do we need to be on the main unity3d thread
                UnityWebRequestAsyncOperation asyncOperation = UnityWebRequestAssetBundle.GetAssetBundle(urlDownloadResponse.DownloadURL, 0).SendWebRequest();

                //TODO: We should render these operations to the loading screen UI.
                asyncOperation.completed += operation =>
                {
                    AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(asyncOperation.webRequest);

                    string[] paths = bundle.GetAllScenePaths();

                    foreach (string p in paths)
                    {
                        Debug.Log($"Found Scene in Bundle: {p}");
                    }

                    AsyncOperation sceneAsync = SceneManager.LoadSceneAsync(System.IO.Path.GetFileNameWithoutExtension(paths.First()));

                    sceneAsync.completed += operation1 =>
                    {
                        //When the scene is finished loading we should cleanup the asset bundle
                        //Don't clean up the WHOLE BUNDLE, just the compressed downloaded data
                        bundle.Unload(false);

                        //TODO: We need a way/system to reference the bundle later so it can be cleaned up inbetween scene loads.
                    };

                    sceneAsync.allowSceneActivation = true;
                };
            }
        }