示例#1
0
        /// <inheritdoc />
        public async Task <HubOnConnectionState> OnConnected(Hub hubConnectedTo)
        {
            //TODO: Verify that the character they requested is owned by them.
            ProjectVersionStage.AssertAlpha();

            NetworkEntityGuid guid = new NetworkEntityGuidBuilder()
                                     .WithId(int.Parse(hubConnectedTo.Context.UserIdentifier))
                                     .WithType(EntityType.Player)
                                     .Build();

            //We may already be able to register.
            if (await TryRegisterGuildStatus(guid, hubConnectedTo.Groups, hubConnectedTo.Context.ConnectionId).ConfigureAwait(false) == HubOnConnectionState.Success)
            {
                return(HubOnConnectionState.Success);
            }

            HubOnConnectionState state = await TryRequestCharacterGuildStatus(guid, hubConnectedTo.Context.UserIdentifier)
                                         .ConfigureAwait(false);

            if (state == HubOnConnectionState.Success)
            {
                return(await TryRegisterGuildStatus(guid, hubConnectedTo.Groups, hubConnectedTo.Context.ConnectionId)
                       .ConfigureAwait(false));
            }

            //Just error, we don't need to abort. Something didn't work right though.
            return(HubOnConnectionState.Error);
        }
示例#2
0
        void Start()
        {
            Debug.Assert(TextObjectTransform != null, nameof(TextObjectTransform) + " != null");
            Debug.Assert(LookTransform != null, nameof(LookTransform) + " != null");

            //TODO: Camera.main is SLOW.
            ProjectVersionStage.AssertAlpha();
            LookTransform = Camera.main.transform;
        }
示例#3
0
        /// <inheritdoc />
        public async Task Start()
        {
            ProjectVersionStage.AssertAlpha();
            //TODO: We need this? Can we add support to GladNet3 to queue up unconnected messages?
            //await Task.Delay(1500);

            //TODO: We're sending with Bearer but there is nothing validating both sides expect that.
            await PayloadSender.SendMessage(new ClientSessionClaimRequestPayload(AuthTokenRepo.RetrieveWithType(), CharacterRepository.CharacterId));
        }
        /// <inheritdoc />
        public override async Task HandleMessage(IPeerSessionMessageContext <GameServerPacketPayload> context, ClientSessionClaimRequestPayload payload)
        {
            //TODO: We need better validation/authorization for clients trying to claim a session. Right now it's open to malicious attack
            ZoneServerTryClaimSessionResponse zoneServerTryClaimSessionResponse = null;

            try
            {
                ProjectVersionStage.AssertAlpha();
                zoneServerTryClaimSessionResponse = await GameServerClient.TryClaimSession(new ZoneServerTryClaimSessionRequest(await GameServerClient.GetAccountIdFromToken(payload.JWT), payload.CharacterId))
                                                    .ConfigureAwait(false);
            }
            catch (Exception e)            //we could get an unauthorized response
            {
                Logger.Error($"Failed to Query for AccountId: {e.Message}. AuthToken provided was: {payload.JWT}");
                throw;
            }

            if (!zoneServerTryClaimSessionResponse.isSuccessful)
            {
                //TODO: Better error code
                await context.PayloadSendService.SendMessage(new ClientSessionClaimResponsePayload(ClientSessionClaimResponseCode.SessionUnavailable))
                .ConfigureAwait(false);

                return;
            }

            NetworkEntityGuidBuilder builder = new NetworkEntityGuidBuilder();

            builder
            .WithId(payload.CharacterId)
            .WithType(EntityType.Player);

            //TODO: We assume they are authenticated, we don't check at the moment but we WILL and SHOULD. Just load their location.
            ZoneServerCharacterLocationResponse locationResponse = await GameServerClient.GetCharacterLocation(payload.CharacterId)
                                                                   .ConfigureAwait(false);

            Vector3 position = locationResponse.isSuccessful ? locationResponse.Position : Vector3.zero;

            if (Logger.IsDebugEnabled)
            {
                Logger.Debug($"Recieved player location: {position}");
            }

            //Just broadcast successful claim, let listeners figure out what to do with this information.
            OnSuccessfulSessionClaimed?.Invoke(this, new PlayerSessionClaimedEventArgs(builder.Build(), position, new PlayerEntitySessionContext(context.PayloadSendService, context.Details.ConnectionId)));

            await context.PayloadSendService.SendMessage(new ClientSessionClaimResponsePayload(ClientSessionClaimResponseCode.Success))
            .ConfigureAwait(false);

            //TODO: We shouldn't hardcode this, we should send the correct scene specified by the gameserver this zone/instance connects to to service.
            await context.PayloadSendService.SendMessage(new LoadNewSceneEventPayload(PlayableGameScene.LobbyType1))
            .ConfigureAwait(false);
        }
        /// <inheritdoc />
        public DefaultLoadableContentResourceManager(
            [NotNull] IContentServerServiceClient contentClient,
            [NotNull] IReadonlyAuthTokenRepository authTokenRepo,
            [NotNull] ILog logger)
        {
            //TODO: We haven't implemented the refcounted cleanup. We ref count, but don't yet dispose.
            ProjectVersionStage.AssertAlpha();

            ContentClient = contentClient ?? throw new ArgumentNullException(nameof(contentClient));
            AuthTokenRepo = authTokenRepo ?? throw new ArgumentNullException(nameof(authTokenRepo));
            Logger        = logger ?? throw new ArgumentNullException(nameof(logger));

            ResourceHandleCache = new Dictionary <long, ReferenceCountedPrefabContentResourceHandle>();

            ReleaseUnmanagedResources();
        }
        //[AuthorizeJwt(GuardianApplicationRole.ZoneServer)] //TODO: Eventually we'll need to auth these zoneservers.
        public async Task <IActionResult> RegisterZoneServer([FromBody] ZoneServerRegisterationRequest registerationRequest,
                                                             [FromServices] IZoneInstanceWorkQueue instanceWorkQueue, [FromServices] IZoneServerRepository zoneRepo)
        {
            //TODO: JSON
            if (!ModelState.IsValid)
            {
                return(BadRequest());
            }

            //Conceptually, just because there is no work doesn't mean this is an error
            //Requesting users who were trying to make an instance could have abandoned that request.
            //Instances may eventually free themselves after inactivity and attempt to reregister instead of shutting down at first
            //So we just want to say "Nothing to do right now" so they can sleep and maybe manually shutdown after a timeout period.
            if (instanceWorkQueue.isEmpty)
            {
                return(NoWorkForInstanceResponse());
            }

            //The specification says this could complete immediately
            //with null if no works exists, there is technically a data race condition between checking isEmpty
            //and trying to dequeue so the result may not be predictible.
            ZoneInstanceWorkEntry zoneInstanceWorkEntry = await instanceWorkQueue.DequeueAsync()
                                                          .ConfigureAwait(false);

            //TODO: If anything here fails after dequeueing we could lose CRITICAL data to keep things running
            //We need VERY good failure handling, and to reenter this work request into the queue somehow.
            //Otherwise the request for the instance will be lost and unhandled forever.
            ProjectVersionStage.AssertAlpha();

            if (zoneInstanceWorkEntry == null)
            {
                return(NoWorkForInstanceResponse());
            }

            //TODO: Validate endpoint
            //Since there IS work to do, we can't just tell the zone instance
            //We must register it into the zone server repo
            if (!await zoneRepo.TryCreateAsync(new ZoneInstanceEntryModel(registerationRequest.ZoneServerEndpoint.EndpointAddress, (short)registerationRequest.ZoneServerEndpoint.EndpointPort, zoneInstanceWorkEntry.WorldId)))
            {
                //As stated above, we need good handling for this else
                //we will encounter MAJOR issues.
                return(NoWorkForInstanceResponse());
            }

            //Success
            return(Ok(new ZoneServerRegisterationResponse(zoneInstanceWorkEntry.WorldId)));
        }
        //[AuthorizeJwt(GuardianApplicationRole.ZoneServer)] //only zone servers should EVER be able to release the active session. They should also likely only be able to release an active session if it's on them.
        public async Task <IActionResult> ReleaseActiveSession([FromRoute(Name = "id")] int characterId)
        {
            //We NEED to AUTH for zoneserver JWT.
            ProjectVersionStage.AssertAlpha();

            //If an active session does NOT exist we have a BIG problem.
            if (!await CharacterSessionRepository.CharacterHasActiveSession(characterId))
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"ZoneServer requested ActiveSession for Player: {characterId} be removed. Session DOES NOT EXIST. This should NOT HAPPEN.");
                }

                return(NotFound());
            }

            //We should try to remove the active sesison.
            //One this active session is revoked the character/account is free to claim any existing session
            //including the same one that was just freed.
            if (!await CharacterSessionRepository.TryDeleteClaimedSession(characterId))
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"ZoneServer requested ActiveSession for Player: {characterId} be removed. Session DOES NOT EXIST. This should NOT HAPPEN.");
                }

                return(BadRequest());
            }
            else
            {
                if (Logger.IsEnabled(LogLevel.Information))
                {
                    Logger.LogInformation($"Removed ActiveSession for Player: {characterId}");
                }

                return(Ok());
            }
        }
        public async Task <IActionResult> TryClaimSession([FromBody] ZoneServerTryClaimSessionRequest request)
        {
            //TODO: Renable auth for session claiming
            ProjectVersionStage.AssertAlpha();

            if (!this.ModelState.IsValid)
            {
                return(BadRequest());                //TODO: Send JSON back too.
            }
            //TODO: We should validate a lot things. One, that the character has a session on this zoneserver.
            //We should also validate that the account owns the character. We need a new auth process for entering users.
            //We have to do this validation, somehow. Or malicious players could spoof this.
            ProjectVersionStage.AssertAlpha();

            //TODO: Verify that the zone id is correct. Right now we aren't providing it and the query doesn't enforce it.
            //We don't validate characterid/accountid association manually. It is implemented in the tryclaim SQL instead.
            //It additionally also checks the zone relation for the session so it will fail if it's invalid for the provided zone.
            //Therefore we don't need to make 3/4 database calls/queries to claim a session. Just one stored procedure call.
            //This is preferable. A result code will be used to indicate the exact error in the future. For now it just fails if it fails.
            bool sessionClaimed = await CharacterSessionRepository.TryClaimUnclaimedSession(request.PlayerAccountId, request.CharacterId);

            return(Ok(new ZoneServerTryClaimSessionResponse(sessionClaimed ? ZoneServerTryClaimSessionResponseCode.Success : ZoneServerTryClaimSessionResponseCode.GeneralServerError)));            //TODO
        }
示例#9
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;
                };
            }
        }
示例#10
0
 /// <inheritdoc />
 public Task <bool> CanUserAccessWorldContet(int userId, long worldId)
 {
     //TODO: When this project leaves alpha, we should probably handle the download authorization validator.
     ProjectVersionStage.AssertAlpha();
     return(Task.FromResult <bool>(true));
 }