Db: lmport

rendering
Liza 2023-02-22 20:29:58 +01:00
parent d5dc55a0c4
commit 7bccec0bae
21 changed files with 505 additions and 116 deletions

View File

@ -115,7 +115,10 @@ namespace Pal.Client.Configuration.Legacy
.Distinct() .Distinct()
.ToList(), .ToList(),
Imported = o.WasImported, // if we have a location not encountered locally, which also wasn't imported,
// it very likely is a download (but we have no information to track this).
Source = o.Seen ? ClientLocation.ESource.SeenLocally :
o.Imports.Count > 0 ? ClientLocation.ESource.Import : ClientLocation.ESource.Download,
SinceVersion = o.SinceVersion ?? "0.0", SinceVersion = o.SinceVersion ?? "0.0",
}; };

View File

@ -0,0 +1,67 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Pal.Client.Configuration;
using Pal.Common;
namespace Pal.Client.Database
{
internal sealed class Cleanup
{
private readonly ILogger<Cleanup> _logger;
private readonly IPalacePalConfiguration _configuration;
public Cleanup(ILogger<Cleanup> logger, IPalacePalConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
public void Purge(PalClientContext dbContext)
{
var toDelete = dbContext.Locations
.Include(o => o.ImportedBy)
.Include(o => o.RemoteEncounters)
.AsSplitQuery()
.Where(DefaultPredicate())
.Where(AnyRemoteEncounter())
.ToList();
_logger.LogInformation("Cleaning up {Count} outdated locations", toDelete.Count);
dbContext.Locations.RemoveRange(toDelete);
}
public void Purge(PalClientContext dbContext, ETerritoryType territoryType)
{
var toDelete = dbContext.Locations
.Include(o => o.ImportedBy)
.Include(o => o.RemoteEncounters)
.AsSplitQuery()
.Where(o => o.TerritoryType == (ushort)territoryType)
.Where(DefaultPredicate())
.Where(AnyRemoteEncounter())
.ToList();
_logger.LogInformation("Cleaning up {Count} outdated locations for territory {Territory}", toDelete.Count,
territoryType);
dbContext.Locations.RemoveRange(toDelete);
}
private Expression<Func<ClientLocation, bool>> DefaultPredicate()
{
return o => !o.Seen &&
o.ImportedBy.Count == 0 &&
o.Source != ClientLocation.ESource.SeenLocally &&
o.Source != ClientLocation.ESource.ExplodedLocally;
}
private Expression<Func<ClientLocation, bool>> AnyRemoteEncounter()
{
if (_configuration.Mode == EMode.Offline)
return o => true;
else
// keep downloaded markers
return o => o.Source != ClientLocation.ESource.Download;
}
}
}

View File

@ -31,9 +31,9 @@ namespace Pal.Client.Database
public List<ImportHistory> ImportedBy { get; set; } = new(); public List<ImportHistory> ImportedBy { get; set; } = new();
/// <summary> /// <summary>
/// Whether this location was originally imported. /// Determines where this location is originally from.
/// </summary> /// </summary>
public bool Imported { get; set; } public ESource Source { get; set; }
/// <summary> /// <summary>
@ -46,5 +46,14 @@ namespace Pal.Client.Database
Trap = 1, Trap = 1,
Hoard = 2, Hoard = 2,
} }
public enum ESource
{
Unknown = 0,
SeenLocally = 1,
ExplodedLocally = 2,
Import = 3,
Download = 4,
}
} }
} }

View File

@ -0,0 +1,148 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Pal.Client.Database;
#nullable disable
namespace Pal.Client.Database.Migrations
{
[DbContext(typeof(PalClientContext))]
[Migration("20230222191929_ChangeLocationImportedToSource")]
partial class ChangeLocationImportedToSource
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.3");
modelBuilder.Entity("ClientLocationImportHistory", b =>
{
b.Property<Guid>("ImportedById")
.HasColumnType("TEXT");
b.Property<int>("ImportedLocationsLocalId")
.HasColumnType("INTEGER");
b.HasKey("ImportedById", "ImportedLocationsLocalId");
b.HasIndex("ImportedLocationsLocalId");
b.ToTable("LocationImports", (string)null);
});
modelBuilder.Entity("Pal.Client.Database.ClientLocation", b =>
{
b.Property<int>("LocalId")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<bool>("Seen")
.HasColumnType("INTEGER");
b.Property<string>("SinceVersion")
.IsRequired()
.HasColumnType("TEXT");
b.Property<int>("Source")
.HasColumnType("INTEGER");
b.Property<ushort>("TerritoryType")
.HasColumnType("INTEGER");
b.Property<int>("Type")
.HasColumnType("INTEGER");
b.Property<float>("X")
.HasColumnType("REAL");
b.Property<float>("Y")
.HasColumnType("REAL");
b.Property<float>("Z")
.HasColumnType("REAL");
b.HasKey("LocalId");
b.ToTable("Locations");
});
modelBuilder.Entity("Pal.Client.Database.ImportHistory", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("ExportedAt")
.HasColumnType("TEXT");
b.Property<DateTime>("ImportedAt")
.HasColumnType("TEXT");
b.Property<string>("RemoteUrl")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Imports");
});
modelBuilder.Entity("Pal.Client.Database.RemoteEncounter", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("AccountId")
.IsRequired()
.HasMaxLength(13)
.HasColumnType("TEXT");
b.Property<int>("ClientLocationId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("ClientLocationId");
b.ToTable("RemoteEncounters");
});
modelBuilder.Entity("ClientLocationImportHistory", b =>
{
b.HasOne("Pal.Client.Database.ImportHistory", null)
.WithMany()
.HasForeignKey("ImportedById")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Pal.Client.Database.ClientLocation", null)
.WithMany()
.HasForeignKey("ImportedLocationsLocalId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Pal.Client.Database.RemoteEncounter", b =>
{
b.HasOne("Pal.Client.Database.ClientLocation", "ClientLocation")
.WithMany("RemoteEncounters")
.HasForeignKey("ClientLocationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("ClientLocation");
});
modelBuilder.Entity("Pal.Client.Database.ClientLocation", b =>
{
b.Navigation("RemoteEncounters");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,28 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Pal.Client.Database.Migrations
{
/// <inheritdoc />
public partial class ChangeLocationImportedToSource : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "Imported",
table: "Locations",
newName: "Source");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "Source",
table: "Locations",
newName: "Imported");
}
}
}

View File

@ -38,9 +38,6 @@ namespace Pal.Client.Database.Migrations
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<bool>("Imported")
.HasColumnType("INTEGER");
b.Property<bool>("Seen") b.Property<bool>("Seen")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
@ -48,6 +45,9 @@ namespace Pal.Client.Database.Migrations
.IsRequired() .IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<int>("Source")
.HasColumnType("INTEGER");
b.Property<ushort>("TerritoryType") b.Property<ushort>("TerritoryType")
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");

View File

@ -14,6 +14,7 @@ using ImGuiNET;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Pal.Client.Configuration; using Pal.Client.Configuration;
using Pal.Client.Database;
using Pal.Client.Extensions; using Pal.Client.Extensions;
using Pal.Client.Floors; using Pal.Client.Floors;
using Pal.Client.Net; using Pal.Client.Net;
@ -390,7 +391,8 @@ namespace Pal.Client.DependencyInjection
{ {
Type = MemoryLocation.EType.Trap, Type = MemoryLocation.EType.Trap,
Position = obj.Position, Position = obj.Position,
Seen = true Seen = true,
Source = ClientLocation.ESource.SeenLocally,
}); });
break; break;
@ -400,7 +402,8 @@ namespace Pal.Client.DependencyInjection
{ {
Type = MemoryLocation.EType.Hoard, Type = MemoryLocation.EType.Hoard,
Position = obj.Position, Position = obj.Position,
Seen = true Seen = true,
Source = ClientLocation.ESource.SeenLocally,
}); });
break; break;
@ -409,7 +412,7 @@ namespace Pal.Client.DependencyInjection
{ {
Type = MemoryLocation.EType.SilverCoffer, Type = MemoryLocation.EType.SilverCoffer,
Position = obj.Position, Position = obj.Position,
Seen = true Seen = true,
}); });
break; break;
} }
@ -425,6 +428,8 @@ namespace Pal.Client.DependencyInjection
Type = MemoryLocation.EType.Trap, Type = MemoryLocation.EType.Trap,
Position = obj.Position, Position = obj.Position,
Seen = true, Seen = true,
Source = ClientLocation.ESource.ExplodedLocally,
}); });
} }
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Account; using Account;
@ -17,50 +18,38 @@ namespace Pal.Client.DependencyInjection
{ {
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly FloorService _floorService; private readonly FloorService _floorService;
private readonly Cleanup _cleanup;
public ImportService(IServiceProvider serviceProvider, FloorService floorService) public ImportService(
IServiceProvider serviceProvider,
FloorService floorService,
Cleanup cleanup)
{ {
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_floorService = floorService; _floorService = floorService;
_cleanup = cleanup;
} }
/*
public void Add(ImportHistory history)
{
using var scope = _serviceProvider.CreateScope();
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
dbContext.Imports.Add(history);
dbContext.SaveChanges();
}
*/
public async Task<ImportHistory?> FindLast(CancellationToken token = default) public async Task<ImportHistory?> FindLast(CancellationToken token = default)
{ {
await using var scope = _serviceProvider.CreateAsyncScope(); await using var scope = _serviceProvider.CreateAsyncScope();
await using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>(); await using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
return await dbContext.Imports.OrderByDescending(x => x.ImportedAt).ThenBy(x => x.Id).FirstOrDefaultAsync(cancellationToken: token); return await dbContext.Imports.OrderByDescending(x => x.ImportedAt).ThenBy(x => x.Id)
.FirstOrDefaultAsync(cancellationToken: token);
} }
/*
public List<ImportHistory> FindForServer(string server)
{
if (string.IsNullOrEmpty(server))
return new();
using var scope = _serviceProvider.CreateScope();
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
return dbContext.Imports.Where(x => x.RemoteUrl == server).ToList();
}*/
public (int traps, int hoard) Import(ExportRoot import) public (int traps, int hoard) Import(ExportRoot import)
{ {
try
{
_floorService.SetToImportState();
using var scope = _serviceProvider.CreateScope(); using var scope = _serviceProvider.CreateScope();
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>(); using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
dbContext.Imports.RemoveRange(dbContext.Imports.Where(x => x.RemoteUrl == import.ServerUrl).ToList()); dbContext.Imports.RemoveRange(dbContext.Imports.Where(x => x.RemoteUrl == import.ServerUrl).ToList());
dbContext.SaveChanges();
ImportHistory importHistory = new ImportHistory ImportHistory importHistory = new ImportHistory
{ {
@ -82,29 +71,96 @@ namespace Pal.Client.DependencyInjection
.ToList() .ToList()
.Select(LoadTerritory.ToMemoryLocation) .Select(LoadTerritory.ToMemoryLocation)
.ToList(); .ToList();
foreach (var newLocation in floor.Objects) foreach (var exportLocation in floor.Objects)
{ {
throw new NotImplementedException(); PersistentLocation persistentLocation = new PersistentLocation
{
Type = ToMemoryType(exportLocation.Type),
Position = new Vector3(exportLocation.X, exportLocation.Y, exportLocation.Z),
Source = ClientLocation.ESource.Unknown,
};
var existingLocation = existingLocations.FirstOrDefault(x => x == persistentLocation);
if (existingLocation != null)
{
var clientLoc = dbContext.Locations.FirstOrDefault(o => o.LocalId == existingLocation.LocalId);
clientLoc?.ImportedBy.Add(importHistory);
continue;
}
ClientLocation clientLocation = new ClientLocation
{
TerritoryType = (ushort)territoryType,
Type = ToClientLocationType(exportLocation.Type),
X = exportLocation.X,
Y = exportLocation.Y,
Z = exportLocation.Z,
Seen = false,
Source = ClientLocation.ESource.Import,
ImportedBy = new List<ImportHistory> { importHistory },
SinceVersion = typeof(Plugin).Assembly.GetName().Version!.ToString(2),
};
dbContext.Locations.Add(clientLocation);
if (exportLocation.Type == ExportObjectType.Trap)
traps++;
else if (exportLocation.Type == ExportObjectType.Hoard)
hoard++;
} }
} }
// TODO filter here, update territories
dbContext.SaveChanges();
_cleanup.Purge(dbContext);
dbContext.SaveChanges(); dbContext.SaveChanges();
_floorService.ResetAll();
return (traps, hoard); return (traps, hoard);
} }
finally
{
_floorService.ResetAll();
}
}
private MemoryLocation.EType ToMemoryType(ExportObjectType exportLocationType)
{
return exportLocationType switch
{
ExportObjectType.Trap => MemoryLocation.EType.Trap,
ExportObjectType.Hoard => MemoryLocation.EType.Hoard,
_ => throw new ArgumentOutOfRangeException(nameof(exportLocationType), exportLocationType, null)
};
}
private ClientLocation.EType ToClientLocationType(ExportObjectType exportLocationType)
{
return exportLocationType switch
{
ExportObjectType.Trap => ClientLocation.EType.Trap,
ExportObjectType.Hoard => ClientLocation.EType.Hoard,
_ => throw new ArgumentOutOfRangeException(nameof(exportLocationType), exportLocationType, null)
};
}
public void RemoveById(Guid id) public void RemoveById(Guid id)
{ {
try
{
_floorService.SetToImportState();
using var scope = _serviceProvider.CreateScope(); using var scope = _serviceProvider.CreateScope();
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>(); using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
dbContext.RemoveRange(dbContext.Imports.Where(x => x.Id == id)); dbContext.RemoveRange(dbContext.Imports.Where(x => x.Id == id));
// TODO filter here, update territories
dbContext.SaveChanges(); dbContext.SaveChanges();
_cleanup.Purge(dbContext);
dbContext.SaveChanges();
}
finally
{
_floorService.ResetAll(); _floorService.ResetAll();
} }
} }
}
} }

View File

@ -96,6 +96,7 @@ namespace Pal.Client
$"Data Source={Path.Join(pluginInterface.GetPluginConfigDirectory(), "palace-pal.data.sqlite3")}"; $"Data Source={Path.Join(pluginInterface.GetPluginConfigDirectory(), "palace-pal.data.sqlite3")}";
services.AddDbContext<PalClientContext>(o => o.UseSqlite(_sqliteConnectionString)); services.AddDbContext<PalClientContext>(o => o.UseSqlite(_sqliteConnectionString));
services.AddTransient<JsonMigration>(); services.AddTransient<JsonMigration>();
services.AddScoped<Cleanup>();
// plugin-specific // plugin-specific
services.AddScoped<DependencyInjectionLoader>(); services.AddScoped<DependencyInjectionLoader>();

View File

@ -55,7 +55,9 @@ namespace Pal.Client
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
await RunMigrations(cancellationToken); await RunMigrations(cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
await RunCleanup(_logger);
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
// v1 migration: config migration for import history, json migration for markers // v1 migration: config migration for import history, json migration for markers
@ -174,9 +176,8 @@ namespace Pal.Client
private async Task RunMigrations(CancellationToken cancellationToken) private async Task RunMigrations(CancellationToken cancellationToken)
{ {
// initialize database await using var scope = _serviceProvider.CreateAsyncScope();
await using (var scope = _serviceProvider.CreateAsyncScope())
{
_logger.LogInformation("Loading database & running migrations"); _logger.LogInformation("Loading database & running migrations");
await using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>(); await using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
@ -184,8 +185,19 @@ namespace Pal.Client
await dbContext.Database.MigrateAsync(cancellationToken); await dbContext.Database.MigrateAsync(cancellationToken);
_logger.LogInformation("Completed database migrations"); _logger.LogInformation("Completed database migrations");
} }
private async Task RunCleanup(ILogger<DependencyInjectionLoader> logger)
{
await using var scope = _serviceProvider.CreateAsyncScope();
await using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
var cleanup = scope.ServiceProvider.GetRequiredService<Cleanup>();
cleanup.Purge(dbContext);
await dbContext.SaveChangesAsync();
} }
public enum ELoadState public enum ELoadState
{ {
Initializing, Initializing,

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Pal.Client.Configuration; using Pal.Client.Configuration;
using Pal.Client.Database;
using Pal.Client.Extensions; using Pal.Client.Extensions;
using Pal.Client.Floors.Tasks; using Pal.Client.Floors.Tasks;
using Pal.Client.Net; using Pal.Client.Net;
@ -14,19 +15,23 @@ namespace Pal.Client.Floors
internal sealed class FloorService internal sealed class FloorService
{ {
private readonly IPalacePalConfiguration _configuration; private readonly IPalacePalConfiguration _configuration;
private readonly Cleanup _cleanup;
private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly IReadOnlyDictionary<ETerritoryType, MemoryTerritory> _territories; private readonly IReadOnlyDictionary<ETerritoryType, MemoryTerritory> _territories;
private ConcurrentBag<EphemeralLocation> _ephemeralLocations = new(); private ConcurrentBag<EphemeralLocation> _ephemeralLocations = new();
public FloorService(IPalacePalConfiguration configuration, IServiceScopeFactory serviceScopeFactory) public FloorService(IPalacePalConfiguration configuration, Cleanup cleanup,
IServiceScopeFactory serviceScopeFactory)
{ {
_configuration = configuration; _configuration = configuration;
_cleanup = cleanup;
_serviceScopeFactory = serviceScopeFactory; _serviceScopeFactory = serviceScopeFactory;
_territories = Enum.GetValues<ETerritoryType>().ToDictionary(o => o, o => new MemoryTerritory(o)); _territories = Enum.GetValues<ETerritoryType>().ToDictionary(o => o, o => new MemoryTerritory(o));
} }
public IReadOnlyCollection<EphemeralLocation> EphemeralLocations => _ephemeralLocations; public IReadOnlyCollection<EphemeralLocation> EphemeralLocations => _ephemeralLocations;
public bool IsImportRunning { get; private set; }
public void ChangeTerritory(ushort territoryType) public void ChangeTerritory(ushort territoryType)
{ {
@ -39,10 +44,10 @@ namespace Pal.Client.Floors
private void ChangeTerritory(ETerritoryType newTerritory) private void ChangeTerritory(ETerritoryType newTerritory)
{ {
var territory = _territories[newTerritory]; var territory = _territories[newTerritory];
if (!territory.IsReady && !territory.IsLoading) if (territory.ReadyState == MemoryTerritory.EReadyState.NotLoaded)
{ {
territory.IsLoading = true; territory.ReadyState = MemoryTerritory.EReadyState.Loading;
new LoadTerritory(_serviceScopeFactory, territory).Start(); new LoadTerritory(_serviceScopeFactory, _cleanup, territory).Start();
} }
} }
@ -57,7 +62,7 @@ namespace Pal.Client.Floors
public MemoryTerritory? GetTerritoryIfReady(ETerritoryType territoryType) public MemoryTerritory? GetTerritoryIfReady(ETerritoryType territoryType)
{ {
var territory = _territories[territoryType]; var territory = _territories[territoryType];
if (!territory.IsReady) if (territory.ReadyState != MemoryTerritory.EReadyState.Ready)
return null; return null;
return territory; return territory;
@ -137,11 +142,22 @@ namespace Pal.Client.Floors
public void ResetAll() public void ResetAll()
{ {
IsImportRunning = false;
foreach (var memoryTerritory in _territories.Values) foreach (var memoryTerritory in _territories.Values)
{ {
lock (memoryTerritory.LockObj) lock (memoryTerritory.LockObj)
memoryTerritory.Reset(); memoryTerritory.Reset();
} }
} }
public void SetToImportState()
{
IsImportRunning = true;
foreach (var memoryTerritory in _territories.Values)
{
lock (memoryTerritory.LockObj)
memoryTerritory.ReadyState = MemoryTerritory.EReadyState.Importing;
}
}
} }
} }

View File

@ -18,8 +18,7 @@ namespace Pal.Client.Floors
} }
public ETerritoryType TerritoryType { get; } public ETerritoryType TerritoryType { get; }
public bool IsReady { get; set; } public EReadyState ReadyState { get; set; } = EReadyState.NotLoaded;
public bool IsLoading { get; set; } // probably merge this with IsReady as enum
public ESyncState SyncState { get; set; } = ESyncState.NotAttempted; public ESyncState SyncState { get; set; } = ESyncState.NotAttempted;
public ConcurrentBag<PersistentLocation> Locations { get; } = new(); public ConcurrentBag<PersistentLocation> Locations { get; } = new();
@ -31,21 +30,34 @@ namespace Pal.Client.Floors
foreach (var location in locations) foreach (var location in locations)
Locations.Add(location); Locations.Add(location);
IsReady = true; ReadyState = EReadyState.Ready;
IsLoading = false;
}
public IEnumerable<PersistentLocation> GetRemovableLocations(EMode mode)
{
// TODO there was better logic here;
return Locations.Where(x => !x.Seen);
} }
public void Reset() public void Reset()
{ {
Locations.Clear(); Locations.Clear();
IsReady = false; SyncState = ESyncState.NotAttempted;
IsLoading = false; ReadyState = EReadyState.NotLoaded;
}
public enum EReadyState
{
NotLoaded,
/// <summary>
/// Currently loading from the database.
/// </summary>
Loading,
/// <summary>
/// Locations loaded, no import running.
/// </summary>
Ready,
/// <summary>
/// Import running, should probably not interact with this too much.
/// </summary>
Importing,
} }
} }
} }

View File

@ -31,6 +31,8 @@ namespace Pal.Client.Floors
/// </summary> /// </summary>
public bool RemoteSeenRequested { get; set; } public bool RemoteSeenRequested { get; set; }
public ClientLocation.ESource Source { get; init; }
public override bool Equals(object? obj) => obj is PersistentLocation && base.Equals(obj); public override bool Equals(object? obj) => obj is PersistentLocation && base.Equals(obj);
public override int GetHashCode() => base.GetHashCode(); public override int GetHashCode() => base.GetHashCode();

View File

@ -1,5 +1,4 @@
using System; using System.Threading.Tasks;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Pal.Client.Database; using Pal.Client.Database;

View File

@ -11,11 +11,15 @@ namespace Pal.Client.Floors.Tasks
{ {
internal sealed class LoadTerritory : DbTask<LoadTerritory> internal sealed class LoadTerritory : DbTask<LoadTerritory>
{ {
private readonly Cleanup _cleanup;
private readonly MemoryTerritory _territory; private readonly MemoryTerritory _territory;
public LoadTerritory(IServiceScopeFactory serviceScopeFactory, MemoryTerritory territory) public LoadTerritory(IServiceScopeFactory serviceScopeFactory,
Cleanup cleanup,
MemoryTerritory territory)
: base(serviceScopeFactory) : base(serviceScopeFactory)
{ {
_cleanup = cleanup;
_territory = territory; _territory = territory;
} }
@ -23,13 +27,19 @@ namespace Pal.Client.Floors.Tasks
{ {
lock (_territory.LockObj) lock (_territory.LockObj)
{ {
if (_territory.IsReady) if (_territory.ReadyState != MemoryTerritory.EReadyState.Loading)
{ {
logger.LogInformation("Territory {Territory} is already loaded", _territory.TerritoryType); logger.LogInformation("Territory {Territory} is in state {State}", _territory.TerritoryType,
_territory.ReadyState);
return; return;
} }
logger.LogInformation("Loading territory {Territory}", _territory.TerritoryType); logger.LogInformation("Loading territory {Territory}", _territory.TerritoryType);
// purge outdated locations
_cleanup.Purge(dbContext, _territory.TerritoryType);
// load good locations
List<ClientLocation> locations = dbContext.Locations List<ClientLocation> locations = dbContext.Locations
.Where(o => o.TerritoryType == (ushort)_territory.TerritoryType) .Where(o => o.TerritoryType == (ushort)_territory.TerritoryType)
.Include(o => o.ImportedBy) .Include(o => o.ImportedBy)
@ -51,6 +61,7 @@ namespace Pal.Client.Floors.Tasks
Type = ToMemoryLocationType(location.Type), Type = ToMemoryLocationType(location.Type),
Position = new Vector3(location.X, location.Y, location.Z), Position = new Vector3(location.X, location.Y, location.Z),
Seen = location.Seen, Seen = location.Seen,
Source = location.Source,
RemoteSeenOn = location.RemoteEncounters.Select(o => o.AccountId).ToList(), RemoteSeenOn = location.RemoteEncounters.Select(o => o.AccountId).ToList(),
}; };
} }

View File

@ -59,6 +59,7 @@ namespace Pal.Client.Floors.Tasks
Y = location.Position.Y, Y = location.Position.Y,
Z = location.Position.Z, Z = location.Position.Z,
Seen = location.Seen, Seen = location.Seen,
Source = location.Source,
SinceVersion = typeof(Plugin).Assembly.GetName().Version!.ToString(2), SinceVersion = typeof(Plugin).Assembly.GetName().Version!.ToString(2),
}; };
} }

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Numerics; using System.Numerics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Pal.Client.Database;
using Pal.Client.Floors; using Pal.Client.Floors;
namespace Pal.Client.Net namespace Pal.Client.Net
@ -69,6 +70,7 @@ namespace Pal.Client.Net
Type = obj.Type.ToMemoryType(), Type = obj.Type.ToMemoryType(),
Position = new Vector3(obj.X, obj.Y, obj.Z), Position = new Vector3(obj.X, obj.Y, obj.Z),
NetworkId = Guid.Parse(obj.NetworkId), NetworkId = Guid.Parse(obj.NetworkId),
Source = ClientLocation.ESource.Download,
}; };
} }

View File

@ -13,7 +13,7 @@ namespace Pal.Client.Net
#if DEBUG #if DEBUG
public const string RemoteUrl = "http://localhost:5415"; public const string RemoteUrl = "http://localhost:5415";
#else #else
//public const string RemoteUrl = "https://pal.liza.sh"; public const string RemoteUrl = "http://localhost:5415";
#endif #endif
private readonly string _userAgent = private readonly string _userAgent =
$"{typeof(RemoteApi).Assembly.GetName().Name?.Replace(" ", "")}/{typeof(RemoteApi).Assembly.GetName().Version?.ToString(2)}"; $"{typeof(RemoteApi).Assembly.GetName().Name?.Replace(" ", "")}/{typeof(RemoteApi).Assembly.GetName().Version?.ToString(2)}";

View File

@ -2,11 +2,11 @@
using Pal.Common; using Pal.Common;
using System; using System;
using System.IO; using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Pal.Client.Database; using Pal.Client.Database;
using Pal.Client.DependencyInjection; using Pal.Client.DependencyInjection;
using Pal.Client.Floors;
using Pal.Client.Properties; using Pal.Client.Properties;
using Pal.Client.Windows; using Pal.Client.Windows;
@ -55,11 +55,15 @@ namespace Pal.Client.Scheduled
if (!Validate(import)) if (!Validate(import))
return; return;
Task.Run(() =>
{
try
{
using (var scope = _serviceScopeFactory.CreateScope()) using (var scope = _serviceScopeFactory.CreateScope())
{ {
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>(); using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
(import.ImportedTraps, import.ImportedHoardCoffers) = _importService.Import(import.Export); (import.ImportedTraps, import.ImportedHoardCoffers) =
_importService.Import(import.Export);
} }
_configWindow.UpdateLastImport(); _configWindow.UpdateLastImport();
@ -71,6 +75,13 @@ namespace Pal.Client.Scheduled
import.ImportedHoardCoffers)); import.ImportedHoardCoffers));
} }
catch (Exception e) catch (Exception e)
{
_logger.LogError(e, "Import failed in inner task");
_chat.Error(string.Format(Localization.Error_ImportFailed, e));
}
});
}
catch (Exception e)
{ {
_logger.LogError(e, "Import failed"); _logger.LogError(e, "Import failed");
_chat.Error(string.Format(Localization.Error_ImportFailed, e)); _chat.Error(string.Format(Localization.Error_ImportFailed, e));

View File

@ -284,7 +284,7 @@ namespace Pal.Client.Windows
null; // only use this once, FileDialogManager will save path between calls null; // only use this once, FileDialogManager will save path between calls
} }
ImGui.BeginDisabled(string.IsNullOrEmpty(_openImportPath) || !File.Exists(_openImportPath)); ImGui.BeginDisabled(string.IsNullOrEmpty(_openImportPath) || !File.Exists(_openImportPath) || _floorService.IsImportRunning);
if (ImGui.Button(Localization.Config_StartImport)) if (ImGui.Button(Localization.Config_StartImport))
DoImport(_openImportPath); DoImport(_openImportPath);
ImGui.EndDisabled(); ImGui.EndDisabled();
@ -298,8 +298,11 @@ namespace Pal.Client.Windows
importHistory.RemoteUrl, importHistory.RemoteUrl,
importHistory.ExportedAt.ToUniversalTime())); importHistory.ExportedAt.ToUniversalTime()));
ImGui.TextWrapped(Localization.Config_UndoImportExplanation2); ImGui.TextWrapped(Localization.Config_UndoImportExplanation2);
ImGui.BeginDisabled(_floorService.IsImportRunning);
if (ImGui.Button(Localization.Config_UndoImport)) if (ImGui.Button(Localization.Config_UndoImport))
UndoImport(importHistory.Id); UndoImport(importHistory.Id);
ImGui.EndDisabled();
} }
ImGui.EndTabItem(); ImGui.EndTabItem();

View File

@ -1,7 +1,10 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
namespace Pal.Common namespace Pal.Common
{ {
[SuppressMessage("ReSharper", "UnusedMember.Global")]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum ETerritoryType : ushort public enum ETerritoryType : ushort
{ {
Palace_1_10 = 561, Palace_1_10 = 561,