From 097ef4a22c5b70dce8244873e1c56233315cd2da Mon Sep 17 00:00:00 2001 From: Zeeshaun Date: Fri, 13 Mar 2026 12:46:30 -0500 Subject: [PATCH] Adding endpoint to only get locations visible to character --- .../Controllers/CharactersController.cs | 2 +- microservices/CharacterApi/DOCUMENTS.md | 1 + microservices/CharacterApi/README.md | 2 +- .../CharacterApi/Services/CharacterStore.cs | 54 ++++++++++++++++++- 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/microservices/CharacterApi/Controllers/CharactersController.cs b/microservices/CharacterApi/Controllers/CharactersController.cs index a28eeb9..bea7bf2 100644 --- a/microservices/CharacterApi/Controllers/CharactersController.cs +++ b/microservices/CharacterApi/Controllers/CharactersController.cs @@ -66,7 +66,7 @@ public class CharactersController : ControllerBase if (character is null) return NotFound(); - var locations = await _characters.GetVisibleLocationsAsync(character); + var locations = await _characters.GetOrCreateVisibleLocationsAsync(character); return Ok(locations); } diff --git a/microservices/CharacterApi/DOCUMENTS.md b/microservices/CharacterApi/DOCUMENTS.md index 9a1ea5e..12c13f5 100644 --- a/microservices/CharacterApi/DOCUMENTS.md +++ b/microservices/CharacterApi/DOCUMENTS.md @@ -41,3 +41,4 @@ Outbound JSON documents } ] ``` + Missing locations within the character's vision window are created automatically before this response is returned. diff --git a/microservices/CharacterApi/README.md b/microservices/CharacterApi/README.md index 0cea884..9eede41 100644 --- a/microservices/CharacterApi/README.md +++ b/microservices/CharacterApi/README.md @@ -6,5 +6,5 @@ See `DOCUMENTS.md` for request payloads and stored document shapes. ## Endpoints - `POST /api/characters` Create a character. - `GET /api/characters` List characters for the current user. -- `GET /api/characters/{id}/visible-locations` List locations visible to that owned character. +- `GET /api/characters/{id}/visible-locations` Ensure and list locations visible to that owned character. - `DELETE /api/characters/{id}` Delete a character owned by the current user. diff --git a/microservices/CharacterApi/Services/CharacterStore.cs b/microservices/CharacterApi/Services/CharacterStore.cs index d159452..97c6005 100644 --- a/microservices/CharacterApi/Services/CharacterStore.cs +++ b/microservices/CharacterApi/Services/CharacterStore.cs @@ -1,5 +1,6 @@ -using CharacterApi.Models; -using MongoDB.Driver; +using CharacterApi.Models; +using MongoDB.Bson; +using MongoDB.Driver; namespace CharacterApi.Services; @@ -41,6 +42,17 @@ public class CharacterStore } public Task> GetVisibleLocationsAsync(Character character) + { + return GetVisibleLocationsInternalAsync(character, ensureGenerated: false); + } + + public async Task> GetOrCreateVisibleLocationsAsync(Character character) + { + await EnsureVisibleLocationsExistAsync(character); + return await GetVisibleLocationsInternalAsync(character, ensureGenerated: true); + } + + private Task> GetVisibleLocationsInternalAsync(Character character, bool ensureGenerated) { var radius = character.VisionRadius > 0 ? character.VisionRadius : 3; var minX = character.Coord.X - radius; @@ -58,6 +70,44 @@ public class CharacterStore return _locations.Find(filter).ToListAsync(); } + private async Task EnsureVisibleLocationsExistAsync(Character character) + { + var radius = character.VisionRadius > 0 ? character.VisionRadius : 3; + + for (var x = character.Coord.X - radius; x <= character.Coord.X + radius; x++) + { + for (var y = character.Coord.Y - radius; y <= character.Coord.Y + radius; y++) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(l => l.Coord.X, x), + Builders.Filter.Eq(l => l.Coord.Y, y) + ); + + var update = Builders.Update + .SetOnInsert(l => l.Name, DefaultLocationName(x, y)) + .SetOnInsert(l => l.Coord, new Coord { X = x, Y = y }) + .SetOnInsert(l => l.Id, ObjectId.GenerateNewId().ToString()) + .SetOnInsert("createdUtc", DateTime.UtcNow); + + try + { + await _locations.UpdateOneAsync(filter, update, new UpdateOptions { IsUpsert = true }); + } + catch (MongoWriteException ex) when (ex.WriteError.Category == ServerErrorCategory.DuplicateKey) + { + // Another request or service instance created it first. + } + } + } + } + + private static string DefaultLocationName(int x, int y) + { + if (x == 0 && y == 0) + return "Origin"; + return $"Location {x},{y}"; + } + public async Task DeleteForOwnerAsync(string id, string ownerUserId, bool allowAnyOwner) { var filter = Builders.Filter.Eq(c => c.Id, id);