Adding upkeep

This commit is contained in:
hz
2026-05-20 04:25:24 -05:00
parent 194dae43da
commit dfbe1d63c0
10 changed files with 530 additions and 40 deletions
@@ -284,6 +284,7 @@ public class LocationService
public async Task<Location?> AssignWorkerAsync(string username, AssignWorkerRequest request)
{
var now = DateTime.UtcNow;
await CompletePendingWorkerAssignmentsAsync(username);
var location = await GetAsync(request.Q, request.R);
if (location is null ||
!string.Equals(location.OwnerUsername, username, StringComparison.OrdinalIgnoreCase) ||
@@ -308,36 +309,19 @@ public class LocationService
var claimed = await _units.UpdateOneAsync(
x => x.Id == unit.Id && x.Status == "Idle",
Builders<UnitInstance>.Update
.Set(x => x.Q, request.Q)
.Set(x => x.R, request.R)
.Set(x => x.Status, "Working")
.Set(x => x.Status, "MovingToWork")
.Set(x => x.MovementRemaining, 0)
.Set(x => x.DestinationQ, request.Q)
.Set(x => x.DestinationR, request.R)
.Set(x => x.MovementStartedAtUtc, now)
.Set(x => x.MovementCompletesAtUtc, now.AddMinutes(Math.Max(1, HexDistance(unit.Q, unit.R, request.Q, request.R))))
.Set(x => x.UpdatedAtUtc, now));
if (claimed.ModifiedCount == 0)
{
return null;
}
var updated = await _locations.FindOneAndUpdateAsync(
x => x.Id == location.Id && (x.WorkerUnitId == null || x.WorkerUnitId == ""),
Builders<Location>.Update
.Set(x => x.WorkerUnitId, unit.Id)
.Set(x => x.WorkerUnitType, "Peasant")
.Set(x => x.LastResourceGeneratedAtUtc, now)
.Set(x => x.UpdatedAtUtc, now),
new FindOneAndUpdateOptions<Location> { ReturnDocument = ReturnDocument.After });
if (updated is null)
{
await _units.UpdateOneAsync(
x => x.Id == unit.Id,
Builders<UnitInstance>.Update
.Set(x => x.Status, "Idle")
.Set(x => x.UpdatedAtUtc, DateTime.UtcNow));
return null;
}
await RecalculateWorkerGenerationAsync(username);
return updated;
return location;
}
public async Task<Location?> DeassignWorkerAsync(string username, DeassignWorkerRequest request)
@@ -433,6 +417,7 @@ public class LocationService
public async Task<List<LocationResponse>> GetVisibleAsync(string username, int? visibilityDepth = null)
{
await CompleteFinishedBuildingsAsync(username);
await CompletePendingWorkerAssignmentsAsync(username);
await RecalculateWorkerGenerationAsync(username);
var ownedLocations = await _locations.Find(x => x.OwnerUsername == username).ToListAsync();
@@ -500,6 +485,7 @@ public class LocationService
public async Task<LocationResponse?> GetVisibleLocationAsync(string username, int q, int r)
{
await CompletePendingWorkerAssignmentsAsync(username);
if (!await CanSeeAsync(username, q, r))
{
return null;
@@ -522,6 +508,98 @@ public class LocationService
return generated.Count == 0 ? null : LocationResponse.FromLocation(generated[0]);
}
private async Task CompletePendingWorkerAssignmentsAsync(string username)
{
var now = DateTime.UtcNow;
var movingWorkers = await _units.Find(x =>
x.Username == username &&
x.UnitType == "Peasant" &&
x.Status == "MovingToWork" &&
x.MovementCompletesAtUtc != null &&
x.MovementCompletesAtUtc <= now)
.ToListAsync();
foreach (var worker in movingWorkers)
{
if (worker.DestinationQ is null || worker.DestinationR is null)
{
await _units.UpdateOneAsync(
x => x.Id == worker.Id,
Builders<UnitInstance>.Update
.Set(x => x.Status, "Idle")
.Unset(x => x.MovementStartedAtUtc)
.Unset(x => x.MovementCompletesAtUtc)
.Set(x => x.UpdatedAtUtc, now));
continue;
}
var destinationQ = worker.DestinationQ.Value;
var destinationR = worker.DestinationR.Value;
var currentQ = worker.Q;
var currentR = worker.R;
var nextTick = worker.MovementCompletesAtUtc!.Value;
while (nextTick <= now && HexDistance(currentQ, currentR, destinationQ, destinationR) > 1)
{
(currentQ, currentR) = NextStepToward(currentQ, currentR, destinationQ, destinationR);
nextTick = nextTick.AddMinutes(1);
}
if (HexDistance(currentQ, currentR, destinationQ, destinationR) > 1)
{
await _units.UpdateOneAsync(
x => x.Id == worker.Id,
Builders<UnitInstance>.Update
.Set(x => x.Q, currentQ)
.Set(x => x.R, currentR)
.Set(x => x.MovementCompletesAtUtc, nextTick)
.Set(x => x.UpdatedAtUtc, now));
continue;
}
var location = await _locations.FindOneAndUpdateAsync(
x => x.OwnerUsername == username &&
x.Q == destinationQ &&
x.R == destinationR &&
!string.IsNullOrWhiteSpace(x.BuildingType) &&
x.BuildingStatus != "Building" &&
(x.WorkerUnitId == null || x.WorkerUnitId == ""),
Builders<Location>.Update
.Set(x => x.WorkerUnitId, worker.Id)
.Set(x => x.WorkerUnitType, "Peasant")
.Set(x => x.LastResourceGeneratedAtUtc, now)
.Set(x => x.UpdatedAtUtc, now),
new FindOneAndUpdateOptions<Location> { ReturnDocument = ReturnDocument.After });
var status = location is null ? "Idle" : "Working";
await _units.UpdateOneAsync(
x => x.Id == worker.Id,
Builders<UnitInstance>.Update
.Set(x => x.Q, destinationQ)
.Set(x => x.R, destinationR)
.Set(x => x.Status, status)
.Set(x => x.LastMovementRefillAtUtc, now)
.Unset(x => x.DestinationQ)
.Unset(x => x.DestinationR)
.Unset(x => x.MovementStartedAtUtc)
.Unset(x => x.MovementCompletesAtUtc)
.Set(x => x.UpdatedAtUtc, now));
}
if (movingWorkers.Count > 0)
{
await RecalculateWorkerGenerationAsync(username);
}
}
private static (int q, int r) NextStepToward(int fromQ, int fromR, int toQ, int toR)
{
return NeighborCoordinates(fromQ, fromR)
.OrderBy(x => HexDistance(x.Q, x.R, toQ, toR))
.ThenBy(x => x.Q)
.ThenBy(x => x.R)
.First();
}
public async Task<bool> CanSeeAsync(string username, int q, int r)
{
var ownedLocations = await _locations.Find(x => x.OwnerUsername == username).ToListAsync();