Merge player database models (#5267)

This commit is contained in:
Javier Guardia Fernández
2021-11-11 17:54:02 +01:00
committed by GitHub
parent 57f1e24466
commit 720e750e86
6 changed files with 94 additions and 168 deletions

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Net;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Content.Server.Database namespace Content.Server.Database
@@ -27,6 +28,7 @@ namespace Content.Server.Database
public DbSet<Preference> Preference { get; set; } = null!; public DbSet<Preference> Preference { get; set; } = null!;
public DbSet<Profile> Profile { get; set; } = null!; public DbSet<Profile> Profile { get; set; } = null!;
public DbSet<AssignedUserId> AssignedUserId { get; set; } = null!; public DbSet<AssignedUserId> AssignedUserId { get; set; } = null!;
public DbSet<Player> Player { get; set; } = default!;
public DbSet<Admin> Admin { get; set; } = null!; public DbSet<Admin> Admin { get; set; } = null!;
public DbSet<AdminRank> AdminRank { get; set; } = null!; public DbSet<AdminRank> AdminRank { get; set; } = null!;
@@ -162,6 +164,22 @@ namespace Content.Server.Database
public Guid UserId { get; set; } public Guid UserId { get; set; }
} }
[Table("player")]
public class Player
{
public int Id { get; set; }
// Permanent data
public Guid UserId { get; set; }
public DateTime FirstSeenTime { get; set; }
// Data that gets updated on each join.
public string LastSeenUserName { get; set; } = null!;
public DateTime LastSeenTime { get; set; }
public IPAddress LastSeenAddress { get; set; } = null!;
public byte[]? LastSeenHWId { get; set; }
}
public class Admin public class Admin
{ {
[Key] public Guid UserId { get; set; } [Key] public Guid UserId { get; set; }

View File

@@ -22,7 +22,6 @@ namespace Content.Server.Database
public DbSet<PostgresServerBan> Ban { get; set; } = default!; public DbSet<PostgresServerBan> Ban { get; set; } = default!;
public DbSet<PostgresServerUnban> Unban { get; set; } = default!; public DbSet<PostgresServerUnban> Unban { get; set; } = default!;
public DbSet<PostgresPlayer> Player { get; set; } = default!;
public DbSet<PostgresConnectionLog> ConnectionLog { get; set; } = default!; public DbSet<PostgresConnectionLog> ConnectionLog { get; set; } = default!;
@@ -70,16 +69,16 @@ namespace Content.Server.Database
.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address") .HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address")
.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL"); .HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR user_id IS NOT NULL OR hwid IS NOT NULL");
modelBuilder.Entity<PostgresPlayer>() modelBuilder.Entity<Player>()
.HasIndex(p => p.UserId) .HasIndex(p => p.UserId)
.IsUnique(); .IsUnique();
// ReSharper disable once StringLiteralTypo // ReSharper disable once StringLiteralTypo
modelBuilder.Entity<PostgresPlayer>() modelBuilder.Entity<Player>()
.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4", .HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4",
"NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address"); "NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address");
modelBuilder.Entity<PostgresPlayer>() modelBuilder.Entity<Player>()
.HasIndex(p => p.LastSeenUserName); .HasIndex(p => p.LastSeenUserName);
modelBuilder.Entity<PostgresConnectionLog>() modelBuilder.Entity<PostgresConnectionLog>()
@@ -131,25 +130,6 @@ namespace Content.Server.Database
public DateTime UnbanTime { get; set; } public DateTime UnbanTime { get; set; }
} }
[Table("player")]
public class PostgresPlayer
{
public int Id { get; set; }
// Permanent data
public Guid UserId { get; set; }
public DateTime FirstSeenTime { get; set; }
// Data that gets updated on each join.
public string LastSeenUserName { get; set; } = null!;
public DateTime LastSeenTime { get; set; }
public IPAddress LastSeenAddress { get; set; } = null!;
public byte[]? LastSeenHWId { get; set; }
}
[Table("connection_log")] [Table("connection_log")]
public class PostgresConnectionLog public class PostgresConnectionLog
{ {

View File

@@ -13,7 +13,6 @@ namespace Content.Server.Database
{ {
public DbSet<SqliteServerBan> Ban { get; set; } = default!; public DbSet<SqliteServerBan> Ban { get; set; } = default!;
public DbSet<SqliteServerUnban> Unban { get; set; } = default!; public DbSet<SqliteServerUnban> Unban { get; set; } = default!;
public DbSet<SqlitePlayer> Player { get; set; } = default!;
public DbSet<SqliteConnectionLog> ConnectionLog { get; set; } = default!; public DbSet<SqliteConnectionLog> ConnectionLog { get; set; } = default!;
public SqliteServerDbContext() public SqliteServerDbContext()
@@ -37,10 +36,18 @@ namespace Content.Server.Database
{ {
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
modelBuilder.Entity<SqlitePlayer>() modelBuilder.Entity<Player>()
.HasIndex(p => p.LastSeenUserName); .HasIndex(p => p.LastSeenUserName);
var converter = new ValueConverter<(IPAddress address, int mask), string>( var ipConverter = new ValueConverter<IPAddress, string>(
v => v.ToString(),
v => IPAddress.Parse(v));
modelBuilder.Entity<Player>()
.Property(p => p.LastSeenAddress)
.HasConversion(ipConverter);
var ipMaskConverter = new ValueConverter<(IPAddress address, int mask), string>(
v => InetToString(v.address, v.mask), v => InetToString(v.address, v.mask),
v => StringToInet(v) v => StringToInet(v)
); );
@@ -49,7 +56,7 @@ namespace Content.Server.Database
.Entity<SqliteServerBan>() .Entity<SqliteServerBan>()
.Property(e => e.Address) .Property(e => e.Address)
.HasColumnType("TEXT") .HasColumnType("TEXT")
.HasConversion(converter); .HasConversion(ipMaskConverter);
} }
public SqliteServerDbContext(DbContextOptions<ServerDbContext> options) : base(options) public SqliteServerDbContext(DbContextOptions<ServerDbContext> options) : base(options)
@@ -105,22 +112,6 @@ namespace Content.Server.Database
public DateTime UnbanTime { get; set; } public DateTime UnbanTime { get; set; }
} }
[Table("player")]
public class SqlitePlayer
{
public int Id { get; set; }
// Permanent data
public Guid UserId { get; set; }
public DateTime FirstSeenTime { get; set; }
// Data that gets updated on each join.
public string LastSeenUserName { get; set; } = null!;
public DateTime LastSeenTime { get; set; }
public string LastSeenAddress { get; set; } = null!;
public byte[]? LastSeenHWId { get; set; }
}
[Table("connection_log")] [Table("connection_log")]
public class SqliteConnectionLog public class SqliteConnectionLog
{ {

View File

@@ -16,7 +16,6 @@ namespace Content.Server.Database
{ {
public abstract class ServerDbBase public abstract class ServerDbBase
{ {
#region Preferences #region Preferences
public async Task<PlayerPreferences?> GetPlayerPreferencesAsync(NetUserId userId) public async Task<PlayerPreferences?> GetPlayerPreferencesAsync(NetUserId userId)
{ {
@@ -229,6 +228,7 @@ namespace Content.Server.Database
} }
#endregion #endregion
#region User Ids
public async Task<NetUserId?> GetAssignedUserIdAsync(string name) public async Task<NetUserId?> GetAssignedUserIdAsync(string name)
{ {
await using var db = await GetDb(); await using var db = await GetDb();
@@ -249,8 +249,9 @@ namespace Content.Server.Database
await db.DbContext.SaveChangesAsync(); await db.DbContext.SaveChangesAsync();
} }
#endregion
#region Bans
/* /*
* BAN STUFF * BAN STUFF
*/ */
@@ -292,18 +293,66 @@ namespace Content.Server.Database
public abstract Task AddServerBanAsync(ServerBanDef serverBan); public abstract Task AddServerBanAsync(ServerBanDef serverBan);
public abstract Task AddServerUnbanAsync(ServerUnbanDef serverUnban); public abstract Task AddServerUnbanAsync(ServerUnbanDef serverUnban);
#endregion
#region Player Records
/* /*
* PLAYER RECORDS * PLAYER RECORDS
*/ */
public abstract Task UpdatePlayerRecord( public async Task UpdatePlayerRecord(
NetUserId userId, NetUserId userId,
string userName, string userName,
IPAddress address, IPAddress address,
ImmutableArray<byte> hwId); ImmutableArray<byte> hwId)
public abstract Task<PlayerRecord?> GetPlayerRecordByUserName(string userName, CancellationToken cancel); {
public abstract Task<PlayerRecord?> GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel); await using var db = await GetDb();
var record = await db.DbContext.Player.SingleOrDefaultAsync(p => p.UserId == userId.UserId);
if (record == null)
{
db.DbContext.Player.Add(record = new Player
{
FirstSeenTime = DateTime.UtcNow,
UserId = userId.UserId,
});
}
record.LastSeenTime = DateTime.UtcNow;
record.LastSeenAddress = address;
record.LastSeenUserName = userName;
record.LastSeenHWId = hwId.ToArray();
await db.DbContext.SaveChangesAsync();
}
public async Task<PlayerRecord?> GetPlayerRecordByUserName(string userName, CancellationToken cancel)
{
await using var db = await GetDb();
// Sort by descending last seen time.
// So if, due to account renames, we have two people with the same username in the DB,
// the most recent one is picked.
var record = await db.DbContext.Player
.OrderByDescending(p => p.LastSeenTime)
.FirstOrDefaultAsync(p => p.LastSeenUserName == userName, cancel);
return record == null ? null : MakePlayerRecord(record);
}
public async Task<PlayerRecord?> GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel)
{
await using var db = await GetDb();
var record = await db.DbContext.Player
.SingleOrDefaultAsync(p => p.UserId == userId.UserId, cancel);
return record == null ? null : MakePlayerRecord(record);
}
protected abstract PlayerRecord MakePlayerRecord(Player player);
#endregion
#region Connection Logs
/* /*
* CONNECTION LOG * CONNECTION LOG
*/ */
@@ -312,9 +361,11 @@ namespace Content.Server.Database
string userName, string userName,
IPAddress address, IPAddress address,
ImmutableArray<byte> hwId); ImmutableArray<byte> hwId);
#endregion
#region Admin Ranks
/* /*
* ADMIN STUFF * ADMIN RANKS
*/ */
public async Task<Admin?> GetAdminDataForAsync(NetUserId userId, CancellationToken cancel) public async Task<Admin?> GetAdminDataForAsync(NetUserId userId, CancellationToken cancel)
{ {
@@ -402,6 +453,7 @@ namespace Content.Server.Database
await db.DbContext.SaveChangesAsync(cancel); await db.DbContext.SaveChangesAsync(cancel);
} }
#endregion
protected abstract Task<DbGuard> GetDb(); protected abstract Task<DbGuard> GetDb();

View File

@@ -9,7 +9,6 @@ using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Robust.Shared.Network; using Robust.Shared.Network;
namespace Content.Server.Database namespace Content.Server.Database
{ {
public sealed class ServerDbPostgres : ServerDbBase public sealed class ServerDbPostgres : ServerDbBase
@@ -222,63 +221,8 @@ namespace Content.Server.Database
await db.PgDbContext.SaveChangesAsync(); await db.PgDbContext.SaveChangesAsync();
} }
public override async Task UpdatePlayerRecord( protected override PlayerRecord MakePlayerRecord(Player record)
NetUserId userId,
string userName,
IPAddress address,
ImmutableArray<byte> hwId)
{ {
await using var db = await GetDbImpl();
var record = await db.PgDbContext.Player.SingleOrDefaultAsync(p => p.UserId == userId.UserId);
if (record == null)
{
db.PgDbContext.Player.Add(record = new PostgresPlayer
{
FirstSeenTime = DateTime.UtcNow,
UserId = userId.UserId,
});
}
record.LastSeenTime = DateTime.UtcNow;
record.LastSeenAddress = address;
record.LastSeenUserName = userName;
record.LastSeenHWId = hwId.ToArray();
await db.PgDbContext.SaveChangesAsync();
}
public override async Task<PlayerRecord?> GetPlayerRecordByUserName(string userName, CancellationToken cancel)
{
await using var db = await GetDbImpl();
// Sort by descending last seen time.
// So if, due to account renames, we have two people with the same username in the DB,
// the most recent one is picked.
var record = await db.PgDbContext.Player
.OrderByDescending(p => p.LastSeenTime)
.FirstOrDefaultAsync(p => p.LastSeenUserName == userName, cancel);
return MakePlayerRecord(record);
}
public override async Task<PlayerRecord?> GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel)
{
await using var db = await GetDbImpl();
var record = await db.PgDbContext.Player
.SingleOrDefaultAsync(p => p.UserId == userId.UserId, cancel);
return MakePlayerRecord(record);
}
private static PlayerRecord? MakePlayerRecord(PostgresPlayer? record)
{
if (record == null)
{
return null;
}
return new PlayerRecord( return new PlayerRecord(
new NetUserId(record.UserId), new NetUserId(record.UserId),
new DateTimeOffset(record.FirstSeenTime), new DateTimeOffset(record.FirstSeenTime),

View File

@@ -1,22 +1,18 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.IP; using Content.Server.IP;
using Content.Server.Preferences;
using Content.Server.Preferences.Managers; using Content.Server.Preferences.Managers;
using Content.Shared;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Network; using Robust.Shared.Network;
namespace Content.Server.Database namespace Content.Server.Database
{ {
/// <summary> /// <summary>
@@ -154,69 +150,14 @@ namespace Content.Server.Database
await db.SqliteDbContext.SaveChangesAsync(); await db.SqliteDbContext.SaveChangesAsync();
} }
public override async Task UpdatePlayerRecord( protected override PlayerRecord MakePlayerRecord(Player record)
NetUserId userId,
string userName,
IPAddress address,
ImmutableArray<byte> hwId)
{ {
await using var db = await GetDbImpl();
var record = await db.SqliteDbContext.Player.SingleOrDefaultAsync(p => p.UserId == userId.UserId);
if (record == null)
{
db.SqliteDbContext.Player.Add(record = new SqlitePlayer
{
FirstSeenTime = DateTime.UtcNow,
UserId = userId.UserId,
});
}
record.LastSeenTime = DateTime.UtcNow;
record.LastSeenAddress = address.ToString();
record.LastSeenUserName = userName;
record.LastSeenHWId = hwId.ToArray();
await db.SqliteDbContext.SaveChangesAsync();
}
public override async Task<PlayerRecord?> GetPlayerRecordByUserName(string userName, CancellationToken cancel)
{
await using var db = await GetDbImpl();
// Sort by descending last seen time.
// So if due to account renames we have two people with the same username in the DB,
// the most recent one is picked.
var record = await db.SqliteDbContext.Player
.OrderByDescending(p => p.LastSeenTime)
.FirstOrDefaultAsync(p => p.LastSeenUserName == userName, cancel);
return MakePlayerRecord(record);
}
public override async Task<PlayerRecord?> GetPlayerRecordByUserId(NetUserId userId, CancellationToken cancel)
{
await using var db = await GetDbImpl();
var record = await db.SqliteDbContext.Player
.SingleOrDefaultAsync(p => p.UserId == userId.UserId, cancel);
return MakePlayerRecord(record);
}
private static PlayerRecord? MakePlayerRecord(SqlitePlayer? record)
{
if (record == null)
{
return null;
}
return new PlayerRecord( return new PlayerRecord(
new NetUserId(record.UserId), new NetUserId(record.UserId),
new DateTimeOffset(record.FirstSeenTime, TimeSpan.Zero), new DateTimeOffset(record.FirstSeenTime, TimeSpan.Zero),
record.LastSeenUserName, record.LastSeenUserName,
new DateTimeOffset(record.LastSeenTime, TimeSpan.Zero), new DateTimeOffset(record.LastSeenTime, TimeSpan.Zero),
IPAddress.Parse(record.LastSeenAddress), record.LastSeenAddress,
record.LastSeenHWId?.ToImmutableArray()); record.LastSeenHWId?.ToImmutableArray());
} }