Persistence fixes

This commit is contained in:
Liza 2024-06-17 00:34:15 +02:00
parent 668287a7e2
commit 90adbdab89
Signed by: liza
GPG Key ID: 7199F8D727D55F67
3 changed files with 44 additions and 14 deletions

View File

@ -95,7 +95,7 @@ internal sealed unsafe class GameHooks : IDisposable
if (!string.IsNullOrEmpty(mapping.PlayerName)) if (!string.IsNullOrEmpty(mapping.PlayerName))
{ {
_logger.LogTrace("Content id {ContentId} belongs to '{Name}'", mapping.ContentId, _logger.LogDebug("Content id {ContentId} belongs to '{Name}'", mapping.ContentId,
mapping.PlayerName); mapping.PlayerName);
mappings.Add(mapping); mappings.Add(mapping);
} }
@ -136,7 +136,7 @@ internal sealed unsafe class GameHooks : IDisposable
/// ///
/// Both 1 and 2 are sent to you on login, unprompted. /// Both 1 and 2 are sent to you on login, unprompted.
/// </summary> /// </summary>
[StructLayout(LayoutKind.Explicit, Size = 0x380)] [StructLayout(LayoutKind.Explicit, Size = 0x420)]
internal struct SocialListResultPage internal struct SocialListResultPage
{ {
[FieldOffset(0x10)] private fixed byte Players[10 * 0x58]; [FieldOffset(0x10)] private fixed byte Players[10 * 0x58];
@ -144,7 +144,7 @@ internal sealed unsafe class GameHooks : IDisposable
public Span<SocialListPlayer> PlayerSpan => new(Unsafe.AsPointer(ref Players[0]), 10); public Span<SocialListPlayer> PlayerSpan => new(Unsafe.AsPointer(ref Players[0]), 10);
} }
[StructLayout(LayoutKind.Explicit, Size = 0x58)] [StructLayout(LayoutKind.Explicit, Size = 0x68, Pack = 1)]
internal struct SocialListPlayer internal struct SocialListPlayer
{ {
/// <summary> /// <summary>
@ -156,6 +156,6 @@ internal sealed unsafe class GameHooks : IDisposable
/// <summary> /// <summary>
/// This *can* be empty, e.g. if you're querying your friend list, the names are ONLY set for characters on the same world. /// This *can* be empty, e.g. if you're querying your friend list, the names are ONLY set for characters on the same world.
/// </summary> /// </summary>
[FieldOffset(0x31)] public fixed byte CharacterName[32]; [FieldOffset(0x3C)] public fixed byte CharacterName[32];
} }
} }

View File

@ -89,29 +89,43 @@ internal sealed class PersistenceContext
WorldId = worldId, WorldId = worldId,
OwnerLocalContentId = l.RetainerOwnerId, OwnerLocalContentId = l.RetainerOwnerId,
}) })
.Where(mapping =>
{
if (mapping.Name == null)
return true;
var currentWorldCache = _worldRetainerCache.GetOrAdd(mapping.WorldId, _ => new());
if (currentWorldCache.TryGetValue(mapping.Name, out ulong playerContentId))
return mapping.OwnerLocalContentId != playerContentId;
return true;
})
.DistinctBy(x => x.LocalContentId)
.ToList(); .ToList();
using var scope = _serviceProvider.CreateScope(); using var scope = _serviceProvider.CreateScope();
using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>(); using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>();
var ids = updates.Select(x => x.LocalContentId).ToList();
var dbRetainers = dbContext.Retainers.Where(x => ids.Contains(x.LocalContentId))
.ToDictionary(x => x.LocalContentId, x => x);
foreach (var retainer in updates) foreach (var retainer in updates)
{ {
if (dbRetainers.TryGetValue(retainer.LocalContentId, out var dbRetainer)) Retainer? dbRetainer = dbContext.Retainers.Find(retainer.LocalContentId);
if (dbRetainer != null)
{ {
_logger.LogDebug("Updating retainer {RetainerName} with {LocalContentId}", retainer.Name, retainer.LocalContentId);
dbRetainer.Name = retainer.Name; dbRetainer.Name = retainer.Name;
dbRetainer.WorldId = retainer.WorldId; dbRetainer.WorldId = retainer.WorldId;
dbRetainer.OwnerLocalContentId = retainer.OwnerLocalContentId; dbRetainer.OwnerLocalContentId = retainer.OwnerLocalContentId;
dbContext.Retainers.Update(dbRetainer); dbContext.Retainers.Update(dbRetainer);
} }
else else
{
_logger.LogDebug("Adding retainer {RetainerName} with {LocalContentId}", retainer.Name, retainer.LocalContentId);
dbContext.Retainers.Add(retainer); dbContext.Retainers.Add(retainer);
}
if (!_playerNameCache.TryGetValue(retainer.OwnerLocalContentId, out string? ownerName)) if (!_playerNameCache.TryGetValue(retainer.OwnerLocalContentId, out string? ownerName))
ownerName = retainer.OwnerLocalContentId.ToString(CultureInfo.InvariantCulture); ownerName = retainer.OwnerLocalContentId.ToString(CultureInfo.InvariantCulture);
_logger.LogTrace("Retainer {RetainerName} belongs to {OwnerId}", retainer.Name, _logger.LogDebug(" Retainer {RetainerName} belongs to {OwnerName}", retainer.Name,
ownerName); ownerName);
if (retainer.Name != null) if (retainer.Name != null)
@ -121,7 +135,9 @@ internal sealed class PersistenceContext
} }
} }
dbContext.SaveChanges(); int changeCount = dbContext.SaveChanges();
if (changeCount > 0)
_logger.LogDebug("Saved {Count} retainer mappings", changeCount);
} }
catch (Exception e) catch (Exception e)
{ {
@ -153,17 +169,31 @@ internal sealed class PersistenceContext
}) })
.ToList(); .ToList();
if (updates.Count == 0)
return;
using (var scope = _serviceProvider.CreateScope()) using (var scope = _serviceProvider.CreateScope())
{ {
using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>(); using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>();
foreach (var update in updates) foreach (var update in updates)
{ {
if (!dbContext.Players.Any(x => x.LocalContentId == update.LocalContentId)) var dbPlayer = dbContext.Players.Find(update.LocalContentId);
dbContext.Players.AddRange(updates); if (dbPlayer == null)
dbContext.Players.Add(update);
else
{
dbPlayer.Name = update.Name;
dbContext.Players.Update(dbPlayer);
}
} }
dbContext.SaveChanges(); int changeCount = dbContext.SaveChanges();
if (changeCount > 0)
{
_logger.LogDebug("Saved {Count} player mappings", changeCount);
foreach (var update in updates)
_logger.LogTrace(" {ContentId} = {Name}", update.LocalContentId, update.Name);
}
} }
foreach (var player in updates) foreach (var player in updates)

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<Version>3.1</Version> <Version>3.2</Version>
<LangVersion>12.0</LangVersion> <LangVersion>12.0</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>