/// <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); }
//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); }
//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; }; } }