Db: lmport
This commit is contained in:
parent
d5dc55a0c4
commit
7bccec0bae
@ -115,7 +115,10 @@ namespace Pal.Client.Configuration.Legacy
|
||||
.Distinct()
|
||||
.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",
|
||||
};
|
||||
|
||||
|
67
Pal.Client/Database/Cleanup.cs
Normal file
67
Pal.Client/Database/Cleanup.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -31,9 +31,9 @@ namespace Pal.Client.Database
|
||||
public List<ImportHistory> ImportedBy { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Whether this location was originally imported.
|
||||
/// Determines where this location is originally from.
|
||||
/// </summary>
|
||||
public bool Imported { get; set; }
|
||||
public ESource Source { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
@ -46,5 +46,14 @@ namespace Pal.Client.Database
|
||||
Trap = 1,
|
||||
Hoard = 2,
|
||||
}
|
||||
|
||||
public enum ESource
|
||||
{
|
||||
Unknown = 0,
|
||||
SeenLocally = 1,
|
||||
ExplodedLocally = 2,
|
||||
Import = 3,
|
||||
Download = 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
148
Pal.Client/Database/Migrations/20230222191929_ChangeLocationImportedToSource.Designer.cs
generated
Normal file
148
Pal.Client/Database/Migrations/20230222191929_ChangeLocationImportedToSource.Designer.cs
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -38,9 +38,6 @@ namespace Pal.Client.Database.Migrations
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Imported")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Seen")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@ -48,6 +45,9 @@ namespace Pal.Client.Database.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Source")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<ushort>("TerritoryType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
@ -14,6 +14,7 @@ using ImGuiNET;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Pal.Client.Configuration;
|
||||
using Pal.Client.Database;
|
||||
using Pal.Client.Extensions;
|
||||
using Pal.Client.Floors;
|
||||
using Pal.Client.Net;
|
||||
@ -390,7 +391,8 @@ namespace Pal.Client.DependencyInjection
|
||||
{
|
||||
Type = MemoryLocation.EType.Trap,
|
||||
Position = obj.Position,
|
||||
Seen = true
|
||||
Seen = true,
|
||||
Source = ClientLocation.ESource.SeenLocally,
|
||||
});
|
||||
break;
|
||||
|
||||
@ -400,7 +402,8 @@ namespace Pal.Client.DependencyInjection
|
||||
{
|
||||
Type = MemoryLocation.EType.Hoard,
|
||||
Position = obj.Position,
|
||||
Seen = true
|
||||
Seen = true,
|
||||
Source = ClientLocation.ESource.SeenLocally,
|
||||
});
|
||||
break;
|
||||
|
||||
@ -409,7 +412,7 @@ namespace Pal.Client.DependencyInjection
|
||||
{
|
||||
Type = MemoryLocation.EType.SilverCoffer,
|
||||
Position = obj.Position,
|
||||
Seen = true
|
||||
Seen = true,
|
||||
});
|
||||
break;
|
||||
}
|
||||
@ -425,6 +428,8 @@ namespace Pal.Client.DependencyInjection
|
||||
Type = MemoryLocation.EType.Trap,
|
||||
Position = obj.Position,
|
||||
Seen = true,
|
||||
Source = ClientLocation.ESource.ExplodedLocally,
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Account;
|
||||
@ -17,94 +18,149 @@ namespace Pal.Client.DependencyInjection
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly FloorService _floorService;
|
||||
private readonly Cleanup _cleanup;
|
||||
|
||||
public ImportService(IServiceProvider serviceProvider, FloorService floorService)
|
||||
public ImportService(
|
||||
IServiceProvider serviceProvider,
|
||||
FloorService floorService,
|
||||
Cleanup cleanup)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_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)
|
||||
{
|
||||
await using var scope = _serviceProvider.CreateAsyncScope();
|
||||
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)
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||
|
||||
dbContext.Imports.RemoveRange(dbContext.Imports.Where(x => x.RemoteUrl == import.ServerUrl).ToList());
|
||||
|
||||
ImportHistory importHistory = new ImportHistory
|
||||
try
|
||||
{
|
||||
Id = Guid.Parse(import.ExportId),
|
||||
RemoteUrl = import.ServerUrl,
|
||||
ExportedAt = import.CreatedAt.ToDateTime(),
|
||||
ImportedAt = DateTime.UtcNow,
|
||||
};
|
||||
dbContext.Imports.Add(importHistory);
|
||||
_floorService.SetToImportState();
|
||||
|
||||
int traps = 0;
|
||||
int hoard = 0;
|
||||
foreach (var floor in import.Floors)
|
||||
{
|
||||
ETerritoryType territoryType = (ETerritoryType)floor.TerritoryType;
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||
|
||||
List<PersistentLocation> existingLocations = dbContext.Locations
|
||||
.Where(loc => loc.TerritoryType == floor.TerritoryType)
|
||||
.ToList()
|
||||
.Select(LoadTerritory.ToMemoryLocation)
|
||||
.ToList();
|
||||
foreach (var newLocation in floor.Objects)
|
||||
dbContext.Imports.RemoveRange(dbContext.Imports.Where(x => x.RemoteUrl == import.ServerUrl).ToList());
|
||||
dbContext.SaveChanges();
|
||||
|
||||
ImportHistory importHistory = new ImportHistory
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
// TODO filter here, update territories
|
||||
dbContext.SaveChanges();
|
||||
Id = Guid.Parse(import.ExportId),
|
||||
RemoteUrl = import.ServerUrl,
|
||||
ExportedAt = import.CreatedAt.ToDateTime(),
|
||||
ImportedAt = DateTime.UtcNow,
|
||||
};
|
||||
dbContext.Imports.Add(importHistory);
|
||||
|
||||
_floorService.ResetAll();
|
||||
return (traps, hoard);
|
||||
int traps = 0;
|
||||
int hoard = 0;
|
||||
foreach (var floor in import.Floors)
|
||||
{
|
||||
ETerritoryType territoryType = (ETerritoryType)floor.TerritoryType;
|
||||
|
||||
List<PersistentLocation> existingLocations = dbContext.Locations
|
||||
.Where(loc => loc.TerritoryType == floor.TerritoryType)
|
||||
.ToList()
|
||||
.Select(LoadTerritory.ToMemoryLocation)
|
||||
.ToList();
|
||||
foreach (var exportLocation in floor.Objects)
|
||||
{
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
dbContext.SaveChanges();
|
||||
|
||||
_cleanup.Purge(dbContext);
|
||||
dbContext.SaveChanges();
|
||||
|
||||
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)
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||
try
|
||||
{
|
||||
_floorService.SetToImportState();
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
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));
|
||||
dbContext.SaveChanges();
|
||||
|
||||
// TODO filter here, update territories
|
||||
dbContext.SaveChanges();
|
||||
|
||||
_floorService.ResetAll();
|
||||
_cleanup.Purge(dbContext);
|
||||
dbContext.SaveChanges();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_floorService.ResetAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,6 +96,7 @@ namespace Pal.Client
|
||||
$"Data Source={Path.Join(pluginInterface.GetPluginConfigDirectory(), "palace-pal.data.sqlite3")}";
|
||||
services.AddDbContext<PalClientContext>(o => o.UseSqlite(_sqliteConnectionString));
|
||||
services.AddTransient<JsonMigration>();
|
||||
services.AddScoped<Cleanup>();
|
||||
|
||||
// plugin-specific
|
||||
services.AddScoped<DependencyInjectionLoader>();
|
||||
|
@ -55,7 +55,9 @@ namespace Pal.Client
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
await RunMigrations(cancellationToken);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
await RunCleanup(_logger);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// v1 migration: config migration for import history, json migration for markers
|
||||
@ -174,18 +176,28 @@ namespace Pal.Client
|
||||
|
||||
private async Task RunMigrations(CancellationToken cancellationToken)
|
||||
{
|
||||
// initialize database
|
||||
await using (var scope = _serviceProvider.CreateAsyncScope())
|
||||
{
|
||||
_logger.LogInformation("Loading database & running migrations");
|
||||
await using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||
await using var scope = _serviceProvider.CreateAsyncScope();
|
||||
|
||||
// takes 2-3 seconds with initializing connections, loading driver etc.
|
||||
await dbContext.Database.MigrateAsync(cancellationToken);
|
||||
_logger.LogInformation("Completed database migrations");
|
||||
}
|
||||
_logger.LogInformation("Loading database & running migrations");
|
||||
await using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||
|
||||
// takes 2-3 seconds with initializing connections, loading driver etc.
|
||||
await dbContext.Database.MigrateAsync(cancellationToken);
|
||||
_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
|
||||
{
|
||||
Initializing,
|
||||
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Pal.Client.Configuration;
|
||||
using Pal.Client.Database;
|
||||
using Pal.Client.Extensions;
|
||||
using Pal.Client.Floors.Tasks;
|
||||
using Pal.Client.Net;
|
||||
@ -14,19 +15,23 @@ namespace Pal.Client.Floors
|
||||
internal sealed class FloorService
|
||||
{
|
||||
private readonly IPalacePalConfiguration _configuration;
|
||||
private readonly Cleanup _cleanup;
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
private readonly IReadOnlyDictionary<ETerritoryType, MemoryTerritory> _territories;
|
||||
|
||||
private ConcurrentBag<EphemeralLocation> _ephemeralLocations = new();
|
||||
|
||||
public FloorService(IPalacePalConfiguration configuration, IServiceScopeFactory serviceScopeFactory)
|
||||
public FloorService(IPalacePalConfiguration configuration, Cleanup cleanup,
|
||||
IServiceScopeFactory serviceScopeFactory)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_cleanup = cleanup;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_territories = Enum.GetValues<ETerritoryType>().ToDictionary(o => o, o => new MemoryTerritory(o));
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<EphemeralLocation> EphemeralLocations => _ephemeralLocations;
|
||||
public bool IsImportRunning { get; private set; }
|
||||
|
||||
public void ChangeTerritory(ushort territoryType)
|
||||
{
|
||||
@ -39,10 +44,10 @@ namespace Pal.Client.Floors
|
||||
private void ChangeTerritory(ETerritoryType newTerritory)
|
||||
{
|
||||
var territory = _territories[newTerritory];
|
||||
if (!territory.IsReady && !territory.IsLoading)
|
||||
if (territory.ReadyState == MemoryTerritory.EReadyState.NotLoaded)
|
||||
{
|
||||
territory.IsLoading = true;
|
||||
new LoadTerritory(_serviceScopeFactory, territory).Start();
|
||||
territory.ReadyState = MemoryTerritory.EReadyState.Loading;
|
||||
new LoadTerritory(_serviceScopeFactory, _cleanup, territory).Start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +62,7 @@ namespace Pal.Client.Floors
|
||||
public MemoryTerritory? GetTerritoryIfReady(ETerritoryType territoryType)
|
||||
{
|
||||
var territory = _territories[territoryType];
|
||||
if (!territory.IsReady)
|
||||
if (territory.ReadyState != MemoryTerritory.EReadyState.Ready)
|
||||
return null;
|
||||
|
||||
return territory;
|
||||
@ -137,11 +142,22 @@ namespace Pal.Client.Floors
|
||||
|
||||
public void ResetAll()
|
||||
{
|
||||
IsImportRunning = false;
|
||||
foreach (var memoryTerritory in _territories.Values)
|
||||
{
|
||||
lock (memoryTerritory.LockObj)
|
||||
memoryTerritory.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetToImportState()
|
||||
{
|
||||
IsImportRunning = true;
|
||||
foreach (var memoryTerritory in _territories.Values)
|
||||
{
|
||||
lock (memoryTerritory.LockObj)
|
||||
memoryTerritory.ReadyState = MemoryTerritory.EReadyState.Importing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,7 @@ namespace Pal.Client.Floors
|
||||
}
|
||||
|
||||
public ETerritoryType TerritoryType { get; }
|
||||
public bool IsReady { get; set; }
|
||||
public bool IsLoading { get; set; } // probably merge this with IsReady as enum
|
||||
public EReadyState ReadyState { get; set; } = EReadyState.NotLoaded;
|
||||
public ESyncState SyncState { get; set; } = ESyncState.NotAttempted;
|
||||
|
||||
public ConcurrentBag<PersistentLocation> Locations { get; } = new();
|
||||
@ -31,21 +30,34 @@ namespace Pal.Client.Floors
|
||||
foreach (var location in locations)
|
||||
Locations.Add(location);
|
||||
|
||||
IsReady = true;
|
||||
IsLoading = false;
|
||||
}
|
||||
|
||||
public IEnumerable<PersistentLocation> GetRemovableLocations(EMode mode)
|
||||
{
|
||||
// TODO there was better logic here;
|
||||
return Locations.Where(x => !x.Seen);
|
||||
ReadyState = EReadyState.Ready;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Locations.Clear();
|
||||
IsReady = false;
|
||||
IsLoading = false;
|
||||
SyncState = ESyncState.NotAttempted;
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ namespace Pal.Client.Floors
|
||||
/// </summary>
|
||||
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 int GetHashCode() => base.GetHashCode();
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Pal.Client.Database;
|
||||
|
@ -11,11 +11,15 @@ namespace Pal.Client.Floors.Tasks
|
||||
{
|
||||
internal sealed class LoadTerritory : DbTask<LoadTerritory>
|
||||
{
|
||||
private readonly Cleanup _cleanup;
|
||||
private readonly MemoryTerritory _territory;
|
||||
|
||||
public LoadTerritory(IServiceScopeFactory serviceScopeFactory, MemoryTerritory territory)
|
||||
public LoadTerritory(IServiceScopeFactory serviceScopeFactory,
|
||||
Cleanup cleanup,
|
||||
MemoryTerritory territory)
|
||||
: base(serviceScopeFactory)
|
||||
{
|
||||
_cleanup = cleanup;
|
||||
_territory = territory;
|
||||
}
|
||||
|
||||
@ -23,13 +27,19 @@ namespace Pal.Client.Floors.Tasks
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
logger.LogInformation("Loading territory {Territory}", _territory.TerritoryType);
|
||||
|
||||
// purge outdated locations
|
||||
_cleanup.Purge(dbContext, _territory.TerritoryType);
|
||||
|
||||
// load good locations
|
||||
List<ClientLocation> locations = dbContext.Locations
|
||||
.Where(o => o.TerritoryType == (ushort)_territory.TerritoryType)
|
||||
.Include(o => o.ImportedBy)
|
||||
@ -51,6 +61,7 @@ namespace Pal.Client.Floors.Tasks
|
||||
Type = ToMemoryLocationType(location.Type),
|
||||
Position = new Vector3(location.X, location.Y, location.Z),
|
||||
Seen = location.Seen,
|
||||
Source = location.Source,
|
||||
RemoteSeenOn = location.RemoteEncounters.Select(o => o.AccountId).ToList(),
|
||||
};
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ namespace Pal.Client.Floors.Tasks
|
||||
Y = location.Position.Y,
|
||||
Z = location.Position.Z,
|
||||
Seen = location.Seen,
|
||||
Source = location.Source,
|
||||
SinceVersion = typeof(Plugin).Assembly.GetName().Version!.ToString(2),
|
||||
};
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Pal.Client.Database;
|
||||
using Pal.Client.Floors;
|
||||
|
||||
namespace Pal.Client.Net
|
||||
@ -69,6 +70,7 @@ namespace Pal.Client.Net
|
||||
Type = obj.Type.ToMemoryType(),
|
||||
Position = new Vector3(obj.X, obj.Y, obj.Z),
|
||||
NetworkId = Guid.Parse(obj.NetworkId),
|
||||
Source = ClientLocation.ESource.Download,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ namespace Pal.Client.Net
|
||||
#if DEBUG
|
||||
public const string RemoteUrl = "http://localhost:5415";
|
||||
#else
|
||||
//public const string RemoteUrl = "https://pal.liza.sh";
|
||||
public const string RemoteUrl = "http://localhost:5415";
|
||||
#endif
|
||||
private readonly string _userAgent =
|
||||
$"{typeof(RemoteApi).Assembly.GetName().Name?.Replace(" ", "")}/{typeof(RemoteApi).Assembly.GetName().Version?.ToString(2)}";
|
||||
|
@ -2,11 +2,11 @@
|
||||
using Pal.Common;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Pal.Client.Database;
|
||||
using Pal.Client.DependencyInjection;
|
||||
using Pal.Client.Floors;
|
||||
using Pal.Client.Properties;
|
||||
using Pal.Client.Windows;
|
||||
|
||||
@ -55,20 +55,31 @@ namespace Pal.Client.Scheduled
|
||||
if (!Validate(import))
|
||||
return;
|
||||
|
||||
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
Task.Run(() =>
|
||||
{
|
||||
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||
(import.ImportedTraps, import.ImportedHoardCoffers) = _importService.Import(import.Export);
|
||||
}
|
||||
try
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||
(import.ImportedTraps, import.ImportedHoardCoffers) =
|
||||
_importService.Import(import.Export);
|
||||
}
|
||||
|
||||
_configWindow.UpdateLastImport();
|
||||
_configWindow.UpdateLastImport();
|
||||
|
||||
_logger.LogInformation(
|
||||
"Imported {ExportId} for {Traps} traps, {Hoard} hoard coffers", import.ExportId,
|
||||
import.ImportedTraps, import.ImportedHoardCoffers);
|
||||
_chat.Message(string.Format(Localization.ImportCompleteStatistics, import.ImportedTraps,
|
||||
import.ImportedHoardCoffers));
|
||||
_logger.LogInformation(
|
||||
"Imported {ExportId} for {Traps} traps, {Hoard} hoard coffers", import.ExportId,
|
||||
import.ImportedTraps, import.ImportedHoardCoffers);
|
||||
_chat.Message(string.Format(Localization.ImportCompleteStatistics, import.ImportedTraps,
|
||||
import.ImportedHoardCoffers));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "Import failed in inner task");
|
||||
_chat.Error(string.Format(Localization.Error_ImportFailed, e));
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -284,7 +284,7 @@ namespace Pal.Client.Windows
|
||||
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))
|
||||
DoImport(_openImportPath);
|
||||
ImGui.EndDisabled();
|
||||
@ -298,8 +298,11 @@ namespace Pal.Client.Windows
|
||||
importHistory.RemoteUrl,
|
||||
importHistory.ExportedAt.ToUniversalTime()));
|
||||
ImGui.TextWrapped(Localization.Config_UndoImportExplanation2);
|
||||
|
||||
ImGui.BeginDisabled(_floorService.IsImportRunning);
|
||||
if (ImGui.Button(Localization.Config_UndoImport))
|
||||
UndoImport(importHistory.Id);
|
||||
ImGui.EndDisabled();
|
||||
}
|
||||
|
||||
ImGui.EndTabItem();
|
||||
|
@ -1,7 +1,10 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Pal.Common
|
||||
{
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
public enum ETerritoryType : ushort
|
||||
{
|
||||
Palace_1_10 = 561,
|
||||
|
Loading…
Reference in New Issue
Block a user