Example #1
0
        public async Task <IActionResult> GetServerEndpoint([FromRoute(Name = "id")] int zoneId)
        {
            if (!ModelState.IsValid)
            {
                return(Json(new ResolveServiceEndpointResponse(ResolveServiceEndpointResponseCode.GeneralRequestError)));
            }

            //We reuse the service discovery response model
            if (!await ZoneRepository.ContainsAsync(zoneId))
            {
                return(Json(new ResolveServiceEndpointResponse(ResolveServiceEndpointResponseCode.ServiceUnlisted)));
            }

            //Small interval for race condition. So we try catch.
            try
            {
                ZoneInstanceEntryModel zone = await ZoneRepository.RetrieveAsync(zoneId);

                //Should be good, we just send them the endpoint
                if (zone != null)
                {
                    return(Ok(new ResolveServiceEndpointResponse(new ResolvedEndpoint(zone.ZoneServerAddress, zone.ZoneServerPort))));
                }
            }
            catch (Exception)
            {
                //TODO: Logging/event
            }

            return(Json(new ResolveServiceEndpointResponse(ResolveServiceEndpointResponseCode.GeneralRequestError)));
        }
Example #2
0
        public async Task <IActionResult> RegisteredZoneCheckin([FromBody] ZoneServerCheckinRequestModel zoneCheckinRequest)
        {
            //This is ok, it means the zone was unregistered before the checkin message was recieved.
            //It doesn't alone indicate a failure.
            if (!await ZoneRepository.ContainsAsync(ClaimsReader.GetAccountIdInt(User)))
            {
                if (Logger.IsEnabled(LogLevel.Warning))
                {
                    Logger.LogWarning($"UserId: {ClaimsReader.GetPlayerAccountId(User)} attempted to checkin ZoneId: {ClaimsReader.GetAccountId(User)} but the zone was not registered.");
                }

                return(Ok());
            }

            ZoneInstanceEntryModel model = await ZoneRepository.RetrieveAsync(ClaimsReader.GetAccountIdInt(User));

            //We don't REMOVE if expired. This should only update checkin time
            //something else should be responsible for removal if expired at some point.
            model.UpdateCheckinTime();
            await ZoneRepository.UpdateAsync(model.ZoneId, model);

            //We don't really have anything to reply to, this shouldn't be called externally.
            //It's basically handling a proxied message queue message.
            return(Ok());
        }
Example #3
0
        public async Task <IActionResult> GetDefaultServerEndpoint()
        {
            if (!ModelState.IsValid)
            {
                return(BuildFailedResponseModel(ResolveServiceEndpointResponseCode.GeneralRequestError));
            }

            //Small interval for race condition. So we try catch.
            try
            {
                ZoneInstanceEntryModel zone = await ZoneRepository.AnyAsync();

                //Should be good, we just send them the endpoint
                if (zone != null)
                {
                    return(BuildSuccessfulResponseModel(new ZoneConnectionEndpointResponse(zone.ZoneId, new ResolvedEndpoint(zone.ZoneServerAddress, zone.ZoneServerPort))));
                }
                else
                {
                    return(BuildFailedResponseModel(ResolveServiceEndpointResponseCode.ServiceUnavailable));
                }
            }
            catch (Exception)
            {
                //TODO: Logging/event
                return(BuildFailedResponseModel(ResolveServiceEndpointResponseCode.GeneralRequestError));
            }
        }
Example #4
0
        /// <inheritdoc />
        public async Task <bool> TryCreateAsync(ZoneInstanceEntryModel model)
        {
#pragma warning disable AsyncFixer02 // Long running or blocking operations under an async method
            Context
            .ZoneEntries
            .Add(model);
#pragma warning restore AsyncFixer02 // Long running or blocking operations under an async method

            return(0 != await Context.SaveChangesAsync());
        }
Example #5
0
        public async Task <IActionResult> GetZoneWorld([FromRoute(Name = "id")] int zoneId)
        {
            if (!await ZoneRepository.ContainsAsync(zoneId).ConfigureAwaitFalse())
            {
                Logger.LogError($"Failed to query for WorldId for Zone: {zoneId}");
                return(NotFound());
            }

            ZoneInstanceEntryModel entryModel = await ZoneRepository.RetrieveAsync(zoneId)
                                                .ConfigureAwaitFalse();

            //We just return the world that this zone is for.
            return(Ok(entryModel.WorldId));
        }
Example #6
0
        public async Task <IActionResult> GetZoneWorldConfiguration([FromRoute(Name = "id")] int zoneId)
        {
            if (!await ZoneRepository.ContainsAsync(zoneId).ConfigureAwaitFalse())
            {
                if (Logger.IsEnabled(LogLevel.Error))
                {
                    Logger.LogError($"Failed to query for WorldId for Zone: {zoneId}");
                }

                return(BuildFailedResponseModel(ZoneWorldConfigurationResponseCode.ZoneDoesntExist));
            }

            ZoneInstanceEntryModel entryModel = await ZoneRepository.RetrieveAsync(zoneId)
                                                .ConfigureAwaitFalse();

            //We just return the world that this zone is for.
            return(BuildSuccessfulResponseModel(new ZoneWorldConfigurationResponse((int)entryModel.WorldId)));
        }
Example #7
0
        public async Task <IActionResult> UpdateCharacterLocation(
            [FromRoute(Name = "id")] int characterId,
            [FromBody] ZoneServerCharacterLocationSaveRequest saveRequest,
            [NotNull][FromServices] ICharacterLocationRepository locationRepository,
            [NotNull][FromServices] IZoneServerRepository zoneRepository)
        {
            if (locationRepository == null)
            {
                throw new ArgumentNullException(nameof(locationRepository));
            }
            if (zoneRepository == null)
            {
                throw new ArgumentNullException(nameof(zoneRepository));
            }

            //TODO: For HTTP callers we should maybe include better information. Though with message queue we can't respond.
            if (!await CheckZoneAuthorizedToModifyCharacterData(characterId))
            {
                return(Forbid());
            }

            //TODO: This could fail if we unregistered. More gracefully handle that case.
            //Get world, we need it for location
            ZoneInstanceEntryModel zoneEntry = await zoneRepository.RetrieveAsync(ClaimsReader.GetAccountIdInt(User));

            //If world was deleted then they won't have location.
            //TODO: Is this the best way to deal with this?
            if (await locationRepository.ContainsAsync(characterId).ConfigureAwaitFalse())
            {
                await locationRepository.UpdateAsync(characterId, BuildCharacterLocationFromSave(characterId, saveRequest, zoneEntry.WorldId))
                .ConfigureAwaitFalseVoid();
            }
            else
            {
                await locationRepository.TryCreateAsync(BuildCharacterLocationFromSave(characterId, saveRequest, zoneEntry.WorldId))
                .ConfigureAwaitFalse();
            }

            return(Ok());
        }
Example #8
0
        /// <inheritdoc />
        public Task UpdateAsync(int key, ZoneInstanceEntryModel model)
        {
            GeneralGenericCrudRepositoryProvider <int, ZoneInstanceEntryModel> crudProvider = new GeneralGenericCrudRepositoryProvider <int, ZoneInstanceEntryModel>(Context.ZoneEntries, Context);

            return(crudProvider.UpdateAsync(key, model));
        }
Example #9
0
		public async Task<CharacterSessionEnterResponse> EnterSession([FromRoute(Name = "id")] int characterId,
			[FromServices] ICharacterLocationRepository characterLocationRepository,
			[FromServices] IZoneServerRepository zoneServerRepository)
		{
			if(!await IsCharacterIdValidForUser(characterId, CharacterRepository))
				return new CharacterSessionEnterResponse(CharacterSessionEnterResponseCode.InvalidCharacterIdError);

			int accountId = ClaimsReader.GetAccountIdInt(User);

			//This checks to see if the account, not just the character, has an active session.
			//We do this before we check anything to reject quick even though the query behind this
			//may be abit more expensive
			//As a note, this checks (or should) CLAIMED SESSIONS. So, it won't prevent multiple session entries for an account
			//This is good because we actually use the left over session data to re-enter the instances on disconnect.
			if(await CharacterSessionRepository.AccountHasActiveSession(accountId))
				return new CharacterSessionEnterResponse(CharacterSessionEnterResponseCode.AccountAlreadyHasCharacterSession);

			//They may have a session entry already, which is ok. So long as they don't have an active claimed session
			//which the above query checks for.
			bool hasSession = await CharacterSessionRepository.ContainsAsync(characterId);

			//We need to check active or not
			if (hasSession)
			{
				//It's possible that the session no longer matches the character's
				//persisted location. We should check their location and put them in the correct zone.

				//If it's active we can just retrieve the data and send them off on their way
				CharacterSessionModel sessionModel = await CharacterSessionRepository.RetrieveAsync(characterId, true);

				if (await characterLocationRepository.ContainsAsync(characterId))
				{
					CharacterLocationModel locationModel = await characterLocationRepository.RetrieveAsync(characterId);
					//They have a location, verify it matches the session
					if (locationModel.WorldId != sessionModel.ZoneEntry.WorldId)
					{
						//The location world and the session's world do not match, so remove the session.
						await CharacterSessionRepository.TryDeleteAsync(sessionModel.CharacterId);
					}
					else
						return new CharacterSessionEnterResponse(sessionModel.ZoneId);
				}
				else
					//TODO: Handle case when we have an inactive session that can be claimed
					return new CharacterSessionEnterResponse(sessionModel.ZoneId);
			}

			//If we didn't return above then we should be in a state where the below can handle this now.

			try
			{
				int targetSessionZoneId = 0;

				//TO know what zone we should connect to we need to check potential
				//character location.
				if (await characterLocationRepository.ContainsAsync(characterId))
				{
					CharacterLocationModel locationModel = await characterLocationRepository.RetrieveAsync(characterId);

					//We have no session so we need to find a zone that matches this server.
					ZoneInstanceEntryModel firstWithWorldId = await zoneServerRepository.FindFirstWithWorldId(locationModel.WorldId);

					//TODO: Should we request one be created for the user??
					//There is NO instance available for this world.
					if (firstWithWorldId == null)
					{
						//Location is basically invalid since there is no running world
						await characterLocationRepository.TryDeleteAsync(locationModel.CharacterId);
					}
					else
					{
						targetSessionZoneId = firstWithWorldId.ZoneId;
					}
				}

				//Try to get into any zone
				if (targetSessionZoneId == 0)
				{
					ZoneInstanceEntryModel entryModel = await zoneServerRepository.AnyAsync();

					if (entryModel != null)
						targetSessionZoneId = entryModel.ZoneId;
				}

				//Still zero means literally no zone servers are available.
				if(targetSessionZoneId == 0)
					return new CharacterSessionEnterResponse(CharacterSessionEnterResponseCode.GeneralServerError);

				if(await CharacterSessionRepository.TryCreateAsync(new CharacterSessionModel(characterId, targetSessionZoneId)))
					return new CharacterSessionEnterResponse(targetSessionZoneId);
				else
					return new CharacterSessionEnterResponse(CharacterSessionEnterResponseCode.GeneralServerError);

			}
			catch (Exception e)
			{
				if(Logger.IsEnabled(LogLevel.Error))
					Logger.LogError($"Character with ID: {characterId} failed to create session. Potentially no default world assigned or World with session was deleted and orphaned. Reason: {e.Message}");
				throw;
			}
		}