127 lines
4.4 KiB
C#
127 lines
4.4 KiB
C#
namespace NeonSprawl.Server.Game.Gathering;
|
|
|
|
/// <summary>PostgreSQL-backed world node instance capacity keyed by interactable id (NEO-61).</summary>
|
|
public sealed class PostgresResourceNodeInstanceStore(Npgsql.NpgsqlDataSource dataSource) : IResourceNodeInstanceStore
|
|
{
|
|
/// <inheritdoc />
|
|
public bool TryGetRemainingGathers(string interactableId, out ResourceNodeInstanceSnapshot snapshot)
|
|
{
|
|
var key = ResourceNodeInstanceIds.Normalize(interactableId);
|
|
if (key.Length == 0)
|
|
{
|
|
snapshot = default;
|
|
return false;
|
|
}
|
|
|
|
PostgresResourceNodeInstanceBootstrap.EnsureSchema(dataSource);
|
|
using var conn = dataSource.OpenConnection();
|
|
using var cmd = new Npgsql.NpgsqlCommand(
|
|
"SELECT remaining_gathers FROM resource_node_instance WHERE interactable_id = @id;",
|
|
conn);
|
|
cmd.Parameters.AddWithValue("id", key);
|
|
var scalar = cmd.ExecuteScalar();
|
|
if (scalar is null or DBNull)
|
|
{
|
|
snapshot = default;
|
|
return false;
|
|
}
|
|
|
|
var remaining = Convert.ToInt32(scalar);
|
|
snapshot = new ResourceNodeInstanceSnapshot(key, remaining);
|
|
return true;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool TryEnsureInitialized(string interactableId, int initialRemainingGathers)
|
|
{
|
|
var key = ResourceNodeInstanceIds.Normalize(interactableId);
|
|
if (key.Length == 0 || initialRemainingGathers < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
PostgresResourceNodeInstanceBootstrap.EnsureSchema(dataSource);
|
|
using var conn = dataSource.OpenConnection();
|
|
using var cmd = new Npgsql.NpgsqlCommand(
|
|
"""
|
|
INSERT INTO resource_node_instance (interactable_id, remaining_gathers, updated_at)
|
|
VALUES (@id, @remaining, now())
|
|
ON CONFLICT (interactable_id) DO NOTHING;
|
|
""",
|
|
conn);
|
|
cmd.Parameters.AddWithValue("id", key);
|
|
cmd.Parameters.AddWithValue("remaining", initialRemainingGathers);
|
|
cmd.ExecuteNonQuery();
|
|
return true;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool TryDecrementRemainingGathers(
|
|
string interactableId,
|
|
out int previousRemaining,
|
|
out int remainingGathers)
|
|
{
|
|
previousRemaining = 0;
|
|
remainingGathers = 0;
|
|
var key = ResourceNodeInstanceIds.Normalize(interactableId);
|
|
if (key.Length == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
PostgresResourceNodeInstanceBootstrap.EnsureSchema(dataSource);
|
|
using var conn = dataSource.OpenConnection();
|
|
using var cmd = new Npgsql.NpgsqlCommand(
|
|
"""
|
|
UPDATE resource_node_instance
|
|
SET remaining_gathers = remaining_gathers - 1,
|
|
updated_at = now()
|
|
WHERE interactable_id = @id
|
|
AND remaining_gathers > 0
|
|
RETURNING remaining_gathers + 1 AS previous_remaining, remaining_gathers;
|
|
""",
|
|
conn);
|
|
cmd.Parameters.AddWithValue("id", key);
|
|
using var reader = cmd.ExecuteReader();
|
|
if (!reader.Read())
|
|
{
|
|
if (TryGetRemainingGathers(key, out var depleted) && depleted.RemainingGathers == 0)
|
|
{
|
|
remainingGathers = 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
previousRemaining = reader.GetInt32(0);
|
|
remainingGathers = reader.GetInt32(1);
|
|
return true;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool TrySetRemainingGathers(string interactableId, int remainingGathers)
|
|
{
|
|
var key = ResourceNodeInstanceIds.Normalize(interactableId);
|
|
if (key.Length == 0 || remainingGathers < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
PostgresResourceNodeInstanceBootstrap.EnsureSchema(dataSource);
|
|
using var conn = dataSource.OpenConnection();
|
|
using var cmd = new Npgsql.NpgsqlCommand(
|
|
"""
|
|
INSERT INTO resource_node_instance (interactable_id, remaining_gathers, updated_at)
|
|
VALUES (@id, @remaining, now())
|
|
ON CONFLICT (interactable_id) DO UPDATE
|
|
SET remaining_gathers = EXCLUDED.remaining_gathers,
|
|
updated_at = now();
|
|
""",
|
|
conn);
|
|
cmd.Parameters.AddWithValue("id", key);
|
|
cmd.Parameters.AddWithValue("remaining", remainingGathers);
|
|
cmd.ExecuteNonQuery();
|
|
return true;
|
|
}
|
|
}
|