using LocationsApi.Models; using MongoDB.Driver; namespace LocationsApi.Services; public class ResourceService { private readonly IMongoCollection _resources; private readonly int _startingGold; private readonly int _headquartersGoldPerMinute; public ResourceService(IConfiguration cfg) { var connectionString = cfg["MongoDB:ConnectionString"] ?? "mongodb://127.0.0.1:27017"; var databaseName = cfg["MongoDB:DatabaseName"] ?? "territory-autobattler"; var client = new MongoClient(connectionString); var database = client.GetDatabase(databaseName); _resources = database.GetCollection("PlayerResources"); _startingGold = int.TryParse(cfg["Resources:StartingGold"], out var startingGold) ? Math.Max(0, startingGold) : 100; _headquartersGoldPerMinute = int.TryParse(cfg["Resources:HeadquartersGoldPerMinute"], out var goldPerMinute) ? Math.Max(0, goldPerMinute) : 10; _resources.Indexes.CreateOne(new CreateIndexModel( Builders.IndexKeys.Ascending(x => x.Username), new CreateIndexOptions { Unique = true })); } public async Task EnsureAsync(string username) { var now = DateTime.UtcNow; var update = Builders.Update .SetOnInsert(x => x.Username, username) .SetOnInsert(x => x.Gold, _startingGold) .SetOnInsert(x => x.CreatedAtUtc, now) .SetOnInsert(x => x.LastGoldGeneratedAtUtc, now) .Set(x => x.GoldPerMinute, _headquartersGoldPerMinute) .Set(x => x.UpdatedAtUtc, now); var resources = await _resources.FindOneAndUpdateAsync( x => x.Username == username, update, new FindOneAndUpdateOptions { IsUpsert = true, ReturnDocument = ReturnDocument.After }); return await ApplyGoldGenerationAsync(resources); } public async Task GetAsync(string username) { var resources = await EnsureAsync(username); return await ApplyGoldGenerationAsync(resources); } private async Task ApplyGoldGenerationAsync(PlayerResources resources) { if (resources.GoldPerMinute <= 0) { return resources; } var now = DateTime.UtcNow; var elapsed = now - resources.LastGoldGeneratedAtUtc; var elapsedMinutes = (int)Math.Floor(elapsed.TotalMinutes); if (elapsedMinutes <= 0) { return resources; } var generatedGold = elapsedMinutes * resources.GoldPerMinute; var generationTimestamp = resources.LastGoldGeneratedAtUtc.AddMinutes(elapsedMinutes); var update = Builders.Update .Inc(x => x.Gold, generatedGold) .Set(x => x.GoldPerMinute, _headquartersGoldPerMinute) .Set(x => x.LastGoldGeneratedAtUtc, generationTimestamp) .Set(x => x.UpdatedAtUtc, now); return await _resources.FindOneAndUpdateAsync( x => x.Username == resources.Username, update, new FindOneAndUpdateOptions { ReturnDocument = ReturnDocument.After }); } }