91 lines
2.8 KiB
C#
91 lines
2.8 KiB
C#
namespace NeonSprawl.Server.Game.PositionState;
|
|
|
|
/// <summary>PostgreSQL-backed <see cref="IPositionStateStore"/> (NS-17). Player ids are stored normalized: invariant lower + trim for case-insensitive parity with <see cref="InMemoryPositionStateStore"/>.</summary>
|
|
public sealed class PostgresPositionStateStore(Npgsql.NpgsqlDataSource dataSource) : IPositionStateStore
|
|
{
|
|
public bool TryGetPosition(string playerId, out PositionSnapshot snapshot)
|
|
{
|
|
var norm = NormalizePlayerId(playerId);
|
|
if (norm.Length == 0)
|
|
{
|
|
snapshot = default;
|
|
return false;
|
|
}
|
|
|
|
PostgresPositionBootstrap.EnsureSchema(dataSource);
|
|
|
|
using var conn = dataSource.OpenConnection();
|
|
using var cmd = new Npgsql.NpgsqlCommand(
|
|
"SELECT pos_x, pos_y, pos_z, sequence FROM player_position WHERE player_id = @pid;",
|
|
conn);
|
|
cmd.Parameters.AddWithValue("pid", norm);
|
|
using var reader = cmd.ExecuteReader();
|
|
if (!reader.Read())
|
|
{
|
|
snapshot = default;
|
|
return false;
|
|
}
|
|
|
|
snapshot = ReadSnapshot(reader);
|
|
return true;
|
|
}
|
|
|
|
public bool TryApplyMoveTarget(string playerId, double x, double y, double z, out PositionSnapshot snapshot)
|
|
{
|
|
var norm = NormalizePlayerId(playerId);
|
|
if (norm.Length == 0)
|
|
{
|
|
snapshot = default;
|
|
return false;
|
|
}
|
|
|
|
PostgresPositionBootstrap.EnsureSchema(dataSource);
|
|
|
|
using var conn = dataSource.OpenConnection();
|
|
using var cmd = new Npgsql.NpgsqlCommand(
|
|
"""
|
|
UPDATE player_position
|
|
SET pos_x = @x, pos_y = @y, pos_z = @z,
|
|
sequence = sequence + 1,
|
|
updated_at = now()
|
|
WHERE player_id = @pid
|
|
RETURNING pos_x, pos_y, pos_z, sequence;
|
|
""",
|
|
conn);
|
|
cmd.Parameters.AddWithValue("pid", norm);
|
|
cmd.Parameters.AddWithValue(nameof(x), x);
|
|
cmd.Parameters.AddWithValue(nameof(y), y);
|
|
cmd.Parameters.AddWithValue(nameof(z), z);
|
|
using var reader = cmd.ExecuteReader();
|
|
if (!reader.Read())
|
|
{
|
|
snapshot = default;
|
|
return false;
|
|
}
|
|
|
|
snapshot = ReadSnapshot(reader);
|
|
return true;
|
|
}
|
|
|
|
private static PositionSnapshot ReadSnapshot(Npgsql.NpgsqlDataReader reader)
|
|
{
|
|
var seq = reader.GetInt64(3);
|
|
return new PositionSnapshot(
|
|
reader.GetDouble(0),
|
|
reader.GetDouble(1),
|
|
reader.GetDouble(2),
|
|
unchecked((ulong)seq));
|
|
}
|
|
|
|
private static string NormalizePlayerId(string? playerId)
|
|
{
|
|
var t = playerId?.Trim();
|
|
if (string.IsNullOrEmpty(t))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
return t.ToLowerInvariant();
|
|
}
|
|
}
|