using CharacterApi.Models; using MongoDB.Driver; namespace CharacterApi.Services; public class CharacterStore { private readonly IMongoCollection _col; public CharacterStore(IConfiguration cfg) { var cs = cfg["MongoDB:ConnectionString"] ?? "mongodb://127.0.0.1:27017"; var dbName = cfg["MongoDB:DatabaseName"] ?? "GameDb"; var client = new MongoClient(cs); var db = client.GetDatabase(dbName); _col = db.GetCollection("Characters"); var ownerIndex = Builders.IndexKeys.Ascending(c => c.OwnerUserId); _col.Indexes.CreateOne(new CreateIndexModel(ownerIndex)); var nameIndex = Builders.IndexKeys.Ascending(c => c.Name); _col.Indexes.CreateOne(new CreateIndexModel(nameIndex, new CreateIndexOptions { Unique = true })); var coordIndex = Builders.IndexKeys.Ascending("Coord.X").Ascending("Coord.Y"); _col.Indexes.CreateOne(new CreateIndexModel(coordIndex)); } public Task CreateAsync(Character character) => _col.InsertOneAsync(character); public Task> GetForOwnerAsync(string ownerUserId) => _col.Find(c => c.OwnerUserId == ownerUserId).ToListAsync(); public async Task GetByIdAsync(string id) => await _col.Find(c => c.Id == id).FirstOrDefaultAsync(); public async Task UpdateCoordAsync(string id, Coord coord) { var filter = Builders.Filter.Eq(c => c.Id, id); var update = Builders.Update .Set(c => c.Coord, coord) .Set(c => c.LastSeenUtc, DateTime.UtcNow); var result = await _col.UpdateOneAsync(filter, update); return result.ModifiedCount > 0 || result.MatchedCount > 0; } public async Task TouchAsync(string id) { var filter = Builders.Filter.Eq(c => c.Id, id); var update = Builders.Update.Set(c => c.LastSeenUtc, DateTime.UtcNow); var result = await _col.UpdateOneAsync(filter, update); return result.ModifiedCount > 0 || result.MatchedCount > 0; } public async Task> GetVisibleOthersAsync(string characterId, int x, int y, int radius, DateTime onlineSinceUtc) { var characters = await _col.Find(c => c.Id != characterId && c.Coord.X >= x - radius && c.Coord.X <= x + radius && c.Coord.Y >= y - radius && c.Coord.Y <= y + radius && c.LastSeenUtc >= onlineSinceUtc) .ToListAsync(); return characters.Select(c => new VisibleCharacter { Id = c.Id ?? string.Empty, Name = c.Name, Coord = c.Coord, LastSeenUtc = c.LastSeenUtc }).ToList(); } public async Task DeleteForOwnerAsync(string id, string ownerUserId, bool allowAnyOwner) { var filter = Builders.Filter.Eq(c => c.Id, id); if (!allowAnyOwner) filter = Builders.Filter.And(filter, Builders.Filter.Eq(c => c.OwnerUserId, ownerUserId)); var result = await _col.DeleteOneAsync(filter); return result.DeletedCount > 0; } }