Db: Move Markers into database
This commit is contained in:
parent
f63e70b0c4
commit
94f3fa2ede
@ -3,6 +3,7 @@ using System.Linq;
|
|||||||
using Dalamud.Game.ClientState;
|
using Dalamud.Game.ClientState;
|
||||||
using Pal.Client.DependencyInjection;
|
using Pal.Client.DependencyInjection;
|
||||||
using Pal.Client.Extensions;
|
using Pal.Client.Extensions;
|
||||||
|
using Pal.Client.Floors;
|
||||||
using Pal.Client.Rendering;
|
using Pal.Client.Rendering;
|
||||||
|
|
||||||
namespace Pal.Client.Commands
|
namespace Pal.Client.Commands
|
||||||
@ -32,30 +33,33 @@ namespace Pal.Client.Commands
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "tnear":
|
case "tnear":
|
||||||
DebugNearest(m => m.Type == Marker.EType.Trap);
|
DebugNearest(m => m.Type == MemoryLocation.EType.Trap);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "hnear":
|
case "hnear":
|
||||||
DebugNearest(m => m.Type == Marker.EType.Hoard);
|
DebugNearest(m => m.Type == MemoryLocation.EType.Hoard);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DebugNearest(Predicate<Marker> predicate)
|
private void DebugNearest(Predicate<PersistentLocation> predicate)
|
||||||
{
|
{
|
||||||
if (!_territoryState.IsInDeepDungeon())
|
if (!_territoryState.IsInDeepDungeon())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var state = _floorService.GetFloorMarkers(_clientState.TerritoryType);
|
var state = _floorService.GetTerritoryIfReady(_clientState.TerritoryType);
|
||||||
|
if (state == null)
|
||||||
|
return;
|
||||||
|
|
||||||
var playerPosition = _clientState.LocalPlayer?.Position;
|
var playerPosition = _clientState.LocalPlayer?.Position;
|
||||||
if (playerPosition == null)
|
if (playerPosition == null)
|
||||||
return;
|
return;
|
||||||
_chat.Message($"{playerPosition}");
|
_chat.Message($"{playerPosition}");
|
||||||
|
|
||||||
var nearbyMarkers = state.Markers
|
var nearbyMarkers = state.Locations
|
||||||
.Where(m => predicate(m))
|
.Where(m => predicate(m))
|
||||||
.Where(m => m.RenderElement != null && m.RenderElement.Color != RenderData.ColorInvisible)
|
.Where(m => m.RenderElement != null && m.RenderElement.Color != RenderData.ColorInvisible)
|
||||||
.Select(m => new { m, distance = (playerPosition - m.Position)?.Length() ?? float.MaxValue })
|
.Select(m => new { m, distance = (playerPosition.Value - m.Position).Length() })
|
||||||
.OrderBy(m => m.distance)
|
.OrderBy(m => m.distance)
|
||||||
.Take(5)
|
.Take(5)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
@ -114,6 +114,9 @@ namespace Pal.Client.Configuration.Legacy
|
|||||||
.Cast<ImportHistory>()
|
.Cast<ImportHistory>()
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToList(),
|
.ToList(),
|
||||||
|
|
||||||
|
Imported = o.WasImported,
|
||||||
|
SinceVersion = o.SinceVersion ?? "0.0",
|
||||||
};
|
};
|
||||||
|
|
||||||
clientLocation.RemoteEncounters = o.RemoteSeenOn
|
clientLocation.RemoteEncounters = o.RemoteSeenOn
|
||||||
|
@ -30,6 +30,17 @@ namespace Pal.Client.Database
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public List<ImportHistory> ImportedBy { get; set; } = new();
|
public List<ImportHistory> ImportedBy { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this location was originally imported.
|
||||||
|
/// </summary>
|
||||||
|
public bool Imported { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// To make rollbacks of local data easier, keep track of the plugin version which was used to create this location initially.
|
||||||
|
/// </summary>
|
||||||
|
public string SinceVersion { get; set; } = "0.0";
|
||||||
|
|
||||||
public enum EType
|
public enum EType
|
||||||
{
|
{
|
||||||
Trap = 1,
|
Trap = 1,
|
||||||
|
148
Pal.Client/Database/Migrations/20230218112804_AddImportedAndSinceVersionToClientLocation.Designer.cs
generated
Normal file
148
Pal.Client/Database/Migrations/20230218112804_AddImportedAndSinceVersionToClientLocation.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("20230218112804_AddImportedAndSinceVersionToClientLocation")]
|
||||||
|
partial class AddImportedAndSinceVersionToClientLocation
|
||||||
|
{
|
||||||
|
/// <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>("Imported")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("Seen")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("SinceVersion")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
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,40 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Pal.Client.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddImportedAndSinceVersionToClientLocation : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "Imported",
|
||||||
|
table: "Locations",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "SinceVersion",
|
||||||
|
table: "Locations",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Imported",
|
||||||
|
table: "Locations");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "SinceVersion",
|
||||||
|
table: "Locations");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -38,9 +38,16 @@ 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");
|
||||||
|
|
||||||
|
b.Property<string>("SinceVersion")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<ushort>("TerritoryType")
|
b.Property<ushort>("TerritoryType")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
@ -58,7 +65,7 @@ namespace Pal.Client.Database.Migrations
|
|||||||
|
|
||||||
b.HasKey("LocalId");
|
b.HasKey("LocalId");
|
||||||
|
|
||||||
b.ToTable("Locations", (string)null);
|
b.ToTable("Locations");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Pal.Client.Database.ImportHistory", b =>
|
modelBuilder.Entity("Pal.Client.Database.ImportHistory", b =>
|
||||||
@ -78,7 +85,7 @@ namespace Pal.Client.Database.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("Imports", (string)null);
|
b.ToTable("Imports");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Pal.Client.Database.RemoteEncounter", b =>
|
modelBuilder.Entity("Pal.Client.Database.RemoteEncounter", b =>
|
||||||
@ -99,7 +106,7 @@ namespace Pal.Client.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("ClientLocationId");
|
b.HasIndex("ClientLocationId");
|
||||||
|
|
||||||
b.ToTable("RemoteEncounters", (string)null);
|
b.ToTable("RemoteEncounters");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("ClientLocationImportHistory", b =>
|
modelBuilder.Entity("ClientLocationImportHistory", b =>
|
||||||
@ -120,13 +127,18 @@ namespace Pal.Client.Database.Migrations
|
|||||||
modelBuilder.Entity("Pal.Client.Database.RemoteEncounter", b =>
|
modelBuilder.Entity("Pal.Client.Database.RemoteEncounter", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Pal.Client.Database.ClientLocation", "ClientLocation")
|
b.HasOne("Pal.Client.Database.ClientLocation", "ClientLocation")
|
||||||
.WithMany()
|
.WithMany("RemoteEncounters")
|
||||||
.HasForeignKey("ClientLocationId")
|
.HasForeignKey("ClientLocationId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.Navigation("ClientLocation");
|
b.Navigation("ClientLocation");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Pal.Client.Database.ClientLocation", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("RemoteEncounters");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
namespace Pal.Client.DependencyInjection
|
|
||||||
{
|
|
||||||
internal sealed class FloorService
|
|
||||||
{
|
|
||||||
public ConcurrentDictionary<ushort, LocalState> FloorMarkers { get; } = new();
|
|
||||||
public ConcurrentBag<Marker> EphemeralMarkers { get; set; } = new();
|
|
||||||
|
|
||||||
public LocalState GetFloorMarkers(ushort territoryType)
|
|
||||||
{
|
|
||||||
return FloorMarkers.GetOrAdd(territoryType, tt => LocalState.Load(tt) ?? new LocalState(tt));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,9 +14,11 @@ using ImGuiNET;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Pal.Client.Configuration;
|
using Pal.Client.Configuration;
|
||||||
using Pal.Client.Extensions;
|
using Pal.Client.Extensions;
|
||||||
|
using Pal.Client.Floors;
|
||||||
using Pal.Client.Net;
|
using Pal.Client.Net;
|
||||||
using Pal.Client.Rendering;
|
using Pal.Client.Rendering;
|
||||||
using Pal.Client.Scheduled;
|
using Pal.Client.Scheduled;
|
||||||
|
using Pal.Common;
|
||||||
|
|
||||||
namespace Pal.Client.DependencyInjection
|
namespace Pal.Client.DependencyInjection
|
||||||
{
|
{
|
||||||
@ -84,44 +86,45 @@ namespace Pal.Client.DependencyInjection
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool recreateLayout = false;
|
bool recreateLayout = false;
|
||||||
bool saveMarkers = false;
|
|
||||||
|
|
||||||
while (EarlyEventQueue.TryDequeue(out IQueueOnFrameworkThread? queued))
|
while (EarlyEventQueue.TryDequeue(out IQueueOnFrameworkThread? queued))
|
||||||
HandleQueued(queued, ref recreateLayout, ref saveMarkers);
|
HandleQueued(queued, ref recreateLayout);
|
||||||
|
|
||||||
if (_territoryState.LastTerritory != _clientState.TerritoryType)
|
if (_territoryState.LastTerritory != _clientState.TerritoryType)
|
||||||
{
|
{
|
||||||
_territoryState.LastTerritory = _clientState.TerritoryType;
|
_territoryState.LastTerritory = _clientState.TerritoryType;
|
||||||
_territoryState.TerritorySyncState = SyncState.NotAttempted;
|
_territoryState.TerritorySyncState = ESyncState.NotAttempted;
|
||||||
NextUpdateObjects.Clear();
|
NextUpdateObjects.Clear();
|
||||||
|
|
||||||
if (_territoryState.IsInDeepDungeon())
|
_floorService.ChangeTerritory(_territoryState.LastTerritory);
|
||||||
_floorService.GetFloorMarkers(_territoryState.LastTerritory);
|
|
||||||
_floorService.EphemeralMarkers.Clear();
|
|
||||||
_territoryState.PomanderOfSight = PomanderState.Inactive;
|
_territoryState.PomanderOfSight = PomanderState.Inactive;
|
||||||
_territoryState.PomanderOfIntuition = PomanderState.Inactive;
|
_territoryState.PomanderOfIntuition = PomanderState.Inactive;
|
||||||
recreateLayout = true;
|
recreateLayout = true;
|
||||||
_debugState.Reset();
|
_debugState.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_territoryState.IsInDeepDungeon())
|
if (!_territoryState.IsInDeepDungeon() || !_floorService.IsReady(_territoryState.LastTerritory))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_configuration.Mode == EMode.Online && _territoryState.TerritorySyncState == SyncState.NotAttempted)
|
if (_configuration.Mode == EMode.Online &&
|
||||||
|
_territoryState.TerritorySyncState == ESyncState.NotAttempted)
|
||||||
{
|
{
|
||||||
_territoryState.TerritorySyncState = SyncState.Started;
|
_territoryState.TerritorySyncState = ESyncState.Started;
|
||||||
Task.Run(async () => await DownloadMarkersForTerritory(_territoryState.LastTerritory));
|
Task.Run(async () => await DownloadMarkersForTerritory(_territoryState.LastTerritory));
|
||||||
}
|
}
|
||||||
|
|
||||||
while (LateEventQueue.TryDequeue(out IQueueOnFrameworkThread? queued))
|
while (LateEventQueue.TryDequeue(out IQueueOnFrameworkThread? queued))
|
||||||
HandleQueued(queued, ref recreateLayout, ref saveMarkers);
|
HandleQueued(queued, ref recreateLayout);
|
||||||
|
|
||||||
var currentFloor = _floorService.GetFloorMarkers(_territoryState.LastTerritory);
|
(IReadOnlyList<PersistentLocation> visiblePersistentMarkers,
|
||||||
|
IReadOnlyList<EphemeralLocation> visibleEphemeralMarkers) =
|
||||||
|
GetRelevantGameObjects();
|
||||||
|
|
||||||
IList<Marker> visibleMarkers = GetRelevantGameObjects();
|
ETerritoryType territoryType = (ETerritoryType)_territoryState.LastTerritory;
|
||||||
HandlePersistentMarkers(currentFloor, visibleMarkers.Where(x => x.IsPermanent()).ToList(), saveMarkers,
|
HandlePersistentLocations(territoryType, visiblePersistentMarkers, recreateLayout);
|
||||||
recreateLayout);
|
|
||||||
HandleEphemeralMarkers(visibleMarkers.Where(x => !x.IsPermanent()).ToList(), recreateLayout);
|
if (_floorService.MergeEphemeralLocations(visibleEphemeralMarkers, recreateLayout))
|
||||||
|
RecreateEphemeralLayout();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -131,142 +134,116 @@ namespace Pal.Client.DependencyInjection
|
|||||||
|
|
||||||
#region Render Markers
|
#region Render Markers
|
||||||
|
|
||||||
private void HandlePersistentMarkers(LocalState currentFloor, IList<Marker> visibleMarkers, bool saveMarkers,
|
private void HandlePersistentLocations(ETerritoryType territoryType,
|
||||||
|
IReadOnlyList<PersistentLocation> visiblePersistentMarkers,
|
||||||
bool recreateLayout)
|
bool recreateLayout)
|
||||||
{
|
{
|
||||||
var currentFloorMarkers = currentFloor.Markers;
|
bool recreatePersistentLocations = _floorService.MergePersistentLocations(
|
||||||
|
territoryType,
|
||||||
bool updateSeenMarkers = false;
|
visiblePersistentMarkers,
|
||||||
var partialAccountId = _configuration.FindAccount(RemoteApi.RemoteUrl)?.AccountId.ToPartialId();
|
recreateLayout,
|
||||||
foreach (var visibleMarker in visibleMarkers)
|
out List<PersistentLocation> locationsToSync);
|
||||||
|
recreatePersistentLocations |= CheckLocationsForPomanders(visiblePersistentMarkers);
|
||||||
|
if (locationsToSync.Count > 0)
|
||||||
{
|
{
|
||||||
Marker? knownMarker = currentFloorMarkers.SingleOrDefault(x => x == visibleMarker);
|
Task.Run(async () =>
|
||||||
if (knownMarker != null)
|
await SyncSeenMarkersForTerritory(_territoryState.LastTerritory, locationsToSync));
|
||||||
{
|
|
||||||
if (!knownMarker.Seen)
|
|
||||||
{
|
|
||||||
knownMarker.Seen = true;
|
|
||||||
saveMarkers = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This requires you to have seen a trap/hoard marker once per floor to synchronize this for older local states,
|
UploadLocations();
|
||||||
// markers discovered afterwards are automatically marked seen.
|
|
||||||
if (partialAccountId != null && knownMarker is { NetworkId: { }, RemoteSeenRequested: false } &&
|
|
||||||
!knownMarker.RemoteSeenOn.Contains(partialAccountId))
|
|
||||||
updateSeenMarkers = true;
|
|
||||||
|
|
||||||
continue;
|
if (recreatePersistentLocations)
|
||||||
|
RecreatePersistentLayout(visiblePersistentMarkers);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentFloorMarkers.Add(visibleMarker);
|
private bool CheckLocationsForPomanders(IReadOnlyList<PersistentLocation> visibleLocations)
|
||||||
recreateLayout = true;
|
{
|
||||||
saveMarkers = true;
|
MemoryTerritory? memoryTerritory = _floorService.GetTerritoryIfReady(_territoryState.LastTerritory);
|
||||||
}
|
if (memoryTerritory is { Locations.Count: > 0 } &&
|
||||||
|
|
||||||
if (!recreateLayout && currentFloorMarkers.Count > 0 &&
|
|
||||||
(_configuration.DeepDungeons.Traps.OnlyVisibleAfterPomander ||
|
(_configuration.DeepDungeons.Traps.OnlyVisibleAfterPomander ||
|
||||||
_configuration.DeepDungeons.HoardCoffers.OnlyVisibleAfterPomander))
|
_configuration.DeepDungeons.HoardCoffers.OnlyVisibleAfterPomander))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var marker in currentFloorMarkers)
|
foreach (var location in memoryTerritory.Locations)
|
||||||
{
|
{
|
||||||
uint desiredColor = DetermineColor(marker, visibleMarkers);
|
uint desiredColor = DetermineColor(location, visibleLocations);
|
||||||
if (marker.RenderElement == null || !marker.RenderElement.IsValid)
|
if (location.RenderElement == null || !location.RenderElement.IsValid)
|
||||||
{
|
return true;
|
||||||
recreateLayout = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (marker.RenderElement.Color != desiredColor)
|
if (location.RenderElement.Color != desiredColor)
|
||||||
marker.RenderElement.Color = desiredColor;
|
location.RenderElement.Color = desiredColor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_debugState.SetFromException(e);
|
_debugState.SetFromException(e);
|
||||||
recreateLayout = true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateSeenMarkers && partialAccountId != null)
|
return false;
|
||||||
{
|
|
||||||
var markersToUpdate = currentFloorMarkers.Where(x =>
|
|
||||||
x is { Seen: true, NetworkId: { }, RemoteSeenRequested: false } &&
|
|
||||||
!x.RemoteSeenOn.Contains(partialAccountId)).ToList();
|
|
||||||
foreach (var marker in markersToUpdate)
|
|
||||||
marker.RemoteSeenRequested = true;
|
|
||||||
Task.Run(async () => await SyncSeenMarkersForTerritory(_territoryState.LastTerritory, markersToUpdate));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (saveMarkers)
|
private void UploadLocations()
|
||||||
{
|
{
|
||||||
currentFloor.Save();
|
MemoryTerritory? memoryTerritory = _floorService.GetTerritoryIfReady(_territoryState.LastTerritory);
|
||||||
|
if (memoryTerritory == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (_territoryState.TerritorySyncState == SyncState.Complete)
|
List<PersistentLocation> locationsToUpload = memoryTerritory.Locations
|
||||||
|
.Where(loc => loc.NetworkId == null && loc.UploadRequested == false)
|
||||||
|
.ToList();
|
||||||
|
if (locationsToUpload.Count > 0)
|
||||||
{
|
{
|
||||||
var markersToUpload = currentFloorMarkers
|
foreach (var location in locationsToUpload)
|
||||||
.Where(x => x.IsPermanent() && x.NetworkId == null && !x.UploadRequested).ToList();
|
location.UploadRequested = true;
|
||||||
if (markersToUpload.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var marker in markersToUpload)
|
|
||||||
marker.UploadRequested = true;
|
|
||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
await UploadMarkersForTerritory(_territoryState.LastTerritory, markersToUpload));
|
await UploadLocationsForTerritory(_territoryState.LastTerritory, locationsToUpload));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recreateLayout)
|
private void RecreatePersistentLayout(IReadOnlyList<PersistentLocation> visibleMarkers)
|
||||||
{
|
{
|
||||||
_renderAdapter.ResetLayer(ELayer.TrapHoard);
|
_renderAdapter.ResetLayer(ELayer.TrapHoard);
|
||||||
|
|
||||||
|
MemoryTerritory? memoryTerritory = _floorService.GetTerritoryIfReady(_territoryState.LastTerritory);
|
||||||
|
if (memoryTerritory == null)
|
||||||
|
return;
|
||||||
|
|
||||||
List<IRenderElement> elements = new();
|
List<IRenderElement> elements = new();
|
||||||
foreach (var marker in currentFloorMarkers)
|
foreach (var location in memoryTerritory.Locations)
|
||||||
{
|
{
|
||||||
if (marker.Seen || _configuration.Mode == EMode.Online ||
|
if (location.Type == MemoryLocation.EType.Trap)
|
||||||
marker is { WasImported: true, Imports.Count: > 0 })
|
|
||||||
{
|
{
|
||||||
if (marker.Type == Marker.EType.Trap)
|
CreateRenderElement(location, elements, DetermineColor(location, visibleMarkers),
|
||||||
{
|
|
||||||
CreateRenderElement(marker, elements, DetermineColor(marker, visibleMarkers),
|
|
||||||
_configuration.DeepDungeons.Traps);
|
_configuration.DeepDungeons.Traps);
|
||||||
}
|
}
|
||||||
else if (marker.Type == Marker.EType.Hoard)
|
else if (location.Type == MemoryLocation.EType.Hoard)
|
||||||
{
|
{
|
||||||
CreateRenderElement(marker, elements, DetermineColor(marker, visibleMarkers),
|
CreateRenderElement(location, elements, DetermineColor(location, visibleMarkers),
|
||||||
_configuration.DeepDungeons.HoardCoffers);
|
_configuration.DeepDungeons.HoardCoffers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (elements.Count == 0)
|
if (elements.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_renderAdapter.SetLayer(ELayer.TrapHoard, elements);
|
_renderAdapter.SetLayer(ELayer.TrapHoard, elements);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleEphemeralMarkers(IList<Marker> visibleMarkers, bool recreateLayout)
|
private void RecreateEphemeralLayout()
|
||||||
{
|
|
||||||
recreateLayout |=
|
|
||||||
_floorService.EphemeralMarkers.Any(existingMarker => visibleMarkers.All(x => x != existingMarker));
|
|
||||||
recreateLayout |=
|
|
||||||
visibleMarkers.Any(visibleMarker => _floorService.EphemeralMarkers.All(x => x != visibleMarker));
|
|
||||||
|
|
||||||
if (recreateLayout)
|
|
||||||
{
|
{
|
||||||
_renderAdapter.ResetLayer(ELayer.RegularCoffers);
|
_renderAdapter.ResetLayer(ELayer.RegularCoffers);
|
||||||
_floorService.EphemeralMarkers.Clear();
|
|
||||||
|
|
||||||
List<IRenderElement> elements = new();
|
List<IRenderElement> elements = new();
|
||||||
foreach (var marker in visibleMarkers)
|
foreach (var location in _floorService.EphemeralLocations)
|
||||||
{
|
{
|
||||||
_floorService.EphemeralMarkers.Add(marker);
|
if (location.Type == MemoryLocation.EType.SilverCoffer &&
|
||||||
|
_configuration.DeepDungeons.SilverCoffers.Show)
|
||||||
if (marker.Type == Marker.EType.SilverCoffer && _configuration.DeepDungeons.SilverCoffers.Show)
|
|
||||||
{
|
{
|
||||||
CreateRenderElement(marker, elements, DetermineColor(marker, visibleMarkers),
|
CreateRenderElement(location, elements, DetermineColor(location),
|
||||||
_configuration.DeepDungeons.SilverCoffers);
|
_configuration.DeepDungeons.SilverCoffers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,38 +253,42 @@ namespace Pal.Client.DependencyInjection
|
|||||||
|
|
||||||
_renderAdapter.SetLayer(ELayer.RegularCoffers, elements);
|
_renderAdapter.SetLayer(ELayer.RegularCoffers, elements);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private uint DetermineColor(Marker marker, IList<Marker> visibleMarkers)
|
private uint DetermineColor(PersistentLocation location, IReadOnlyList<PersistentLocation> visibleLocations)
|
||||||
{
|
{
|
||||||
switch (marker.Type)
|
switch (location.Type)
|
||||||
{
|
{
|
||||||
case Marker.EType.Trap when _territoryState.PomanderOfSight == PomanderState.Inactive ||
|
case MemoryLocation.EType.Trap
|
||||||
|
when _territoryState.PomanderOfSight == PomanderState.Inactive ||
|
||||||
!_configuration.DeepDungeons.Traps.OnlyVisibleAfterPomander ||
|
!_configuration.DeepDungeons.Traps.OnlyVisibleAfterPomander ||
|
||||||
visibleMarkers.Any(x => x == marker):
|
visibleLocations.Any(x => x == location):
|
||||||
return _configuration.DeepDungeons.Traps.Color;
|
return _configuration.DeepDungeons.Traps.Color;
|
||||||
case Marker.EType.Hoard when _territoryState.PomanderOfIntuition == PomanderState.Inactive ||
|
case MemoryLocation.EType.Hoard
|
||||||
|
when _territoryState.PomanderOfIntuition == PomanderState.Inactive ||
|
||||||
!_configuration.DeepDungeons.HoardCoffers.OnlyVisibleAfterPomander ||
|
!_configuration.DeepDungeons.HoardCoffers.OnlyVisibleAfterPomander ||
|
||||||
visibleMarkers.Any(x => x == marker):
|
visibleLocations.Any(x => x == location):
|
||||||
return _configuration.DeepDungeons.HoardCoffers.Color;
|
return _configuration.DeepDungeons.HoardCoffers.Color;
|
||||||
case Marker.EType.SilverCoffer:
|
|
||||||
return _configuration.DeepDungeons.SilverCoffers.Color;
|
|
||||||
case Marker.EType.Trap:
|
|
||||||
case Marker.EType.Hoard:
|
|
||||||
return RenderData.ColorInvisible;
|
|
||||||
default:
|
default:
|
||||||
return ImGui.ColorConvertFloat4ToU32(new Vector4(1, 0.5f, 1, 0.4f));
|
return RenderData.ColorInvisible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateRenderElement(Marker marker, List<IRenderElement> elements, uint color,
|
private uint DetermineColor(EphemeralLocation location)
|
||||||
|
{
|
||||||
|
if (location.Type == MemoryLocation.EType.SilverCoffer)
|
||||||
|
return _configuration.DeepDungeons.SilverCoffers.Color;
|
||||||
|
|
||||||
|
return RenderData.ColorInvisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateRenderElement(MemoryLocation location, List<IRenderElement> elements, uint color,
|
||||||
MarkerConfiguration config)
|
MarkerConfiguration config)
|
||||||
{
|
{
|
||||||
if (!config.Show)
|
if (!config.Show)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var element = _renderAdapter.CreateElement(marker.Type, marker.Position, color, config.Fill);
|
var element = _renderAdapter.CreateElement(location.Type, location.Position, color, config.Fill);
|
||||||
marker.RenderElement = element;
|
location.RenderElement = element;
|
||||||
elements.Add(element);
|
elements.Add(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,7 +306,7 @@ namespace Pal.Client.DependencyInjection
|
|||||||
Type = SyncType.Download,
|
Type = SyncType.Download,
|
||||||
TerritoryType = territoryId,
|
TerritoryType = territoryId,
|
||||||
Success = success,
|
Success = success,
|
||||||
Markers = downloadedMarkers
|
Locations = downloadedMarkers
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -334,17 +315,17 @@ namespace Pal.Client.DependencyInjection
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UploadMarkersForTerritory(ushort territoryId, List<Marker> markersToUpload)
|
private async Task UploadLocationsForTerritory(ushort territoryId, List<PersistentLocation> locationsToUpload)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var (success, uploadedMarkers) = await _remoteApi.UploadMarker(territoryId, markersToUpload);
|
var (success, uploadedLocations) = await _remoteApi.UploadLocations(territoryId, locationsToUpload);
|
||||||
LateEventQueue.Enqueue(new QueuedSyncResponse
|
LateEventQueue.Enqueue(new QueuedSyncResponse
|
||||||
{
|
{
|
||||||
Type = SyncType.Upload,
|
Type = SyncType.Upload,
|
||||||
TerritoryType = territoryId,
|
TerritoryType = territoryId,
|
||||||
Success = success,
|
Success = success,
|
||||||
Markers = uploadedMarkers
|
Locations = uploadedLocations
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -353,17 +334,18 @@ namespace Pal.Client.DependencyInjection
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SyncSeenMarkersForTerritory(ushort territoryId, List<Marker> markersToUpdate)
|
private async Task SyncSeenMarkersForTerritory(ushort territoryId,
|
||||||
|
IReadOnlyList<PersistentLocation> locationsToUpdate)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var success = await _remoteApi.MarkAsSeen(territoryId, markersToUpdate);
|
var success = await _remoteApi.MarkAsSeen(territoryId, locationsToUpdate);
|
||||||
LateEventQueue.Enqueue(new QueuedSyncResponse
|
LateEventQueue.Enqueue(new QueuedSyncResponse
|
||||||
{
|
{
|
||||||
Type = SyncType.MarkSeen,
|
Type = SyncType.MarkSeen,
|
||||||
TerritoryType = territoryId,
|
TerritoryType = territoryId,
|
||||||
Success = success,
|
Success = success,
|
||||||
Markers = markersToUpdate,
|
Locations = locationsToUpdate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -374,9 +356,10 @@ namespace Pal.Client.DependencyInjection
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private IList<Marker> GetRelevantGameObjects()
|
private (IReadOnlyList<PersistentLocation>, IReadOnlyList<EphemeralLocation>) GetRelevantGameObjects()
|
||||||
{
|
{
|
||||||
List<Marker> result = new();
|
List<PersistentLocation> persistentLocations = new();
|
||||||
|
List<EphemeralLocation> ephemeralLocations = new();
|
||||||
for (int i = 246; i < _objectTable.Length; i++)
|
for (int i = 246; i < _objectTable.Length; i++)
|
||||||
{
|
{
|
||||||
GameObject? obj = _objectTable[i];
|
GameObject? obj = _objectTable[i];
|
||||||
@ -391,16 +374,31 @@ namespace Pal.Client.DependencyInjection
|
|||||||
case 2007185:
|
case 2007185:
|
||||||
case 2007186:
|
case 2007186:
|
||||||
case 2009504:
|
case 2009504:
|
||||||
result.Add(new Marker(Marker.EType.Trap, obj.Position) { Seen = true });
|
persistentLocations.Add(new PersistentLocation
|
||||||
|
{
|
||||||
|
Type = MemoryLocation.EType.Trap,
|
||||||
|
Position = obj.Position,
|
||||||
|
Seen = true
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2007542:
|
case 2007542:
|
||||||
case 2007543:
|
case 2007543:
|
||||||
result.Add(new Marker(Marker.EType.Hoard, obj.Position) { Seen = true });
|
persistentLocations.Add(new PersistentLocation
|
||||||
|
{
|
||||||
|
Type = MemoryLocation.EType.Hoard,
|
||||||
|
Position = obj.Position,
|
||||||
|
Seen = true
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2007357:
|
case 2007357:
|
||||||
result.Add(new Marker(Marker.EType.SilverCoffer, obj.Position) { Seen = true });
|
ephemeralLocations.Add(new EphemeralLocation
|
||||||
|
{
|
||||||
|
Type = MemoryLocation.EType.SilverCoffer,
|
||||||
|
Position = obj.Position,
|
||||||
|
Seen = true
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -409,18 +407,25 @@ namespace Pal.Client.DependencyInjection
|
|||||||
{
|
{
|
||||||
var obj = _objectTable.FirstOrDefault(x => x.Address == address);
|
var obj = _objectTable.FirstOrDefault(x => x.Address == address);
|
||||||
if (obj != null && obj.Position.Length() > 0.1)
|
if (obj != null && obj.Position.Length() > 0.1)
|
||||||
result.Add(new Marker(Marker.EType.Trap, obj.Position) { Seen = true });
|
{
|
||||||
|
persistentLocations.Add(new PersistentLocation
|
||||||
|
{
|
||||||
|
Type = MemoryLocation.EType.Trap,
|
||||||
|
Position = obj.Position,
|
||||||
|
Seen = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return (persistentLocations, ephemeralLocations);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleQueued(IQueueOnFrameworkThread queued, ref bool recreateLayout, ref bool saveMarkers)
|
private void HandleQueued(IQueueOnFrameworkThread queued, ref bool recreateLayout)
|
||||||
{
|
{
|
||||||
Type handlerType = typeof(IQueueOnFrameworkThread.Handler<>).MakeGenericType(queued.GetType());
|
Type handlerType = typeof(IQueueOnFrameworkThread.Handler<>).MakeGenericType(queued.GetType());
|
||||||
var handler = (IQueueOnFrameworkThread.IHandler)_serviceProvider.GetRequiredService(handlerType);
|
var handler = (IQueueOnFrameworkThread.IHandler)_serviceProvider.GetRequiredService(handlerType);
|
||||||
|
|
||||||
handler.RunIfCompatible(queued, ref recreateLayout, ref saveMarkers);
|
handler.RunIfCompatible(queued, ref recreateLayout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,21 +3,28 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Account;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Pal.Client.Database;
|
using Pal.Client.Database;
|
||||||
|
using Pal.Client.Floors;
|
||||||
|
using Pal.Client.Floors.Tasks;
|
||||||
|
using Pal.Common;
|
||||||
|
|
||||||
namespace Pal.Client.DependencyInjection
|
namespace Pal.Client.DependencyInjection
|
||||||
{
|
{
|
||||||
internal sealed class ImportService
|
internal sealed class ImportService
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
private readonly FloorService _floorService;
|
||||||
|
|
||||||
public ImportService(IServiceProvider serviceProvider)
|
public ImportService(IServiceProvider serviceProvider, FloorService floorService)
|
||||||
{
|
{
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
|
_floorService = floorService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
public void Add(ImportHistory history)
|
public void Add(ImportHistory history)
|
||||||
{
|
{
|
||||||
using var scope = _serviceProvider.CreateScope();
|
using var scope = _serviceProvider.CreateScope();
|
||||||
@ -26,6 +33,7 @@ namespace Pal.Client.DependencyInjection
|
|||||||
dbContext.Imports.Add(history);
|
dbContext.Imports.Add(history);
|
||||||
dbContext.SaveChanges();
|
dbContext.SaveChanges();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
public async Task<ImportHistory?> FindLast(CancellationToken token = default)
|
public async Task<ImportHistory?> FindLast(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
@ -35,6 +43,7 @@ namespace Pal.Client.DependencyInjection
|
|||||||
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)
|
public List<ImportHistory> FindForServer(string server)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(server))
|
if (string.IsNullOrEmpty(server))
|
||||||
@ -44,18 +53,58 @@ namespace Pal.Client.DependencyInjection
|
|||||||
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||||
|
|
||||||
return dbContext.Imports.Where(x => x.RemoteUrl == server).ToList();
|
return dbContext.Imports.Where(x => x.RemoteUrl == server).ToList();
|
||||||
}
|
}*/
|
||||||
|
|
||||||
public void RemoveAllByIds(List<Guid> ids)
|
public (int traps, int hoard) Import(ExportRoot import)
|
||||||
{
|
{
|
||||||
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 => ids.Contains(x.Id)));
|
dbContext.Imports.RemoveRange(dbContext.Imports.Where(x => x.RemoteUrl == import.ServerUrl).ToList());
|
||||||
|
|
||||||
|
ImportHistory importHistory = new ImportHistory
|
||||||
|
{
|
||||||
|
Id = Guid.Parse(import.ExportId),
|
||||||
|
RemoteUrl = import.ServerUrl,
|
||||||
|
ExportedAt = import.CreatedAt.ToDateTime(),
|
||||||
|
ImportedAt = DateTime.UtcNow,
|
||||||
|
};
|
||||||
|
dbContext.Imports.Add(importHistory);
|
||||||
|
|
||||||
|
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 newLocation in floor.Objects)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO filter here, update territories
|
||||||
dbContext.SaveChanges();
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
_floorService.ResetAll();
|
||||||
|
return (traps, hoard);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveById(Guid id)
|
public void RemoveById(Guid id)
|
||||||
=> RemoveAllByIds(new List<Guid> { id });
|
{
|
||||||
|
using var scope = _serviceProvider.CreateScope();
|
||||||
|
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||||
|
|
||||||
|
dbContext.RemoveRange(dbContext.Imports.Where(x => x.Id == id));
|
||||||
|
|
||||||
|
// TODO filter here, update territories
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
_floorService.ResetAll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ namespace Pal.Client.DependencyInjection
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ushort LastTerritory { get; set; }
|
public ushort LastTerritory { get; set; }
|
||||||
public SyncState TerritorySyncState { get; set; }
|
public ESyncState TerritorySyncState { get; set; }
|
||||||
public PomanderState PomanderOfSight { get; set; } = PomanderState.Inactive;
|
public PomanderState PomanderOfSight { get; set; } = PomanderState.Inactive;
|
||||||
public PomanderState PomanderOfIntuition { get; set; } = PomanderState.Inactive;
|
public PomanderState PomanderOfIntuition { get; set; } = PomanderState.Inactive;
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ using Pal.Client.Database;
|
|||||||
using Pal.Client.DependencyInjection;
|
using Pal.Client.DependencyInjection;
|
||||||
using Pal.Client.DependencyInjection.Logging;
|
using Pal.Client.DependencyInjection.Logging;
|
||||||
using Pal.Client.Extensions;
|
using Pal.Client.Extensions;
|
||||||
|
using Pal.Client.Floors;
|
||||||
using Pal.Client.Net;
|
using Pal.Client.Net;
|
||||||
using Pal.Client.Properties;
|
using Pal.Client.Properties;
|
||||||
using Pal.Client.Rendering;
|
using Pal.Client.Rendering;
|
||||||
@ -63,7 +64,8 @@ namespace Pal.Client
|
|||||||
CommandManager commandManager,
|
CommandManager commandManager,
|
||||||
DataManager dataManager)
|
DataManager dataManager)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Building service container");
|
_logger.LogInformation("Building service container for {Assembly}",
|
||||||
|
typeof(DependencyInjectionContext).Assembly.FullName);
|
||||||
|
|
||||||
// set up legacy services
|
// set up legacy services
|
||||||
#pragma warning disable CS0612
|
#pragma warning disable CS0612
|
||||||
|
24
Pal.Client/Floors/EphemeralLocation.cs
Normal file
24
Pal.Client/Floors/EphemeralLocation.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Pal.Client.Floors
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This is a currently-visible marker.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class EphemeralLocation : MemoryLocation
|
||||||
|
{
|
||||||
|
public override bool Equals(object? obj) => obj is EphemeralLocation && base.Equals(obj);
|
||||||
|
|
||||||
|
public override int GetHashCode() => base.GetHashCode();
|
||||||
|
|
||||||
|
public static bool operator ==(EphemeralLocation? a, object? b)
|
||||||
|
{
|
||||||
|
return Equals(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(EphemeralLocation? a, object? b)
|
||||||
|
{
|
||||||
|
return !Equals(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
147
Pal.Client/Floors/FloorService.cs
Normal file
147
Pal.Client/Floors/FloorService.cs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Pal.Client.Configuration;
|
||||||
|
using Pal.Client.Extensions;
|
||||||
|
using Pal.Client.Floors.Tasks;
|
||||||
|
using Pal.Client.Net;
|
||||||
|
using Pal.Common;
|
||||||
|
|
||||||
|
namespace Pal.Client.Floors
|
||||||
|
{
|
||||||
|
internal sealed class FloorService
|
||||||
|
{
|
||||||
|
private readonly IPalacePalConfiguration _configuration;
|
||||||
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||||
|
private readonly IReadOnlyDictionary<ETerritoryType, MemoryTerritory> _territories;
|
||||||
|
|
||||||
|
private ConcurrentBag<EphemeralLocation> _ephemeralLocations = new();
|
||||||
|
|
||||||
|
public FloorService(IPalacePalConfiguration configuration, IServiceScopeFactory serviceScopeFactory)
|
||||||
|
{
|
||||||
|
_configuration = configuration;
|
||||||
|
_serviceScopeFactory = serviceScopeFactory;
|
||||||
|
_territories = Enum.GetValues<ETerritoryType>().ToDictionary(o => o, o => new MemoryTerritory(o));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyCollection<EphemeralLocation> EphemeralLocations => _ephemeralLocations;
|
||||||
|
|
||||||
|
public void ChangeTerritory(ushort territoryType)
|
||||||
|
{
|
||||||
|
_ephemeralLocations = new ConcurrentBag<EphemeralLocation>();
|
||||||
|
|
||||||
|
if (typeof(ETerritoryType).IsEnumDefined(territoryType))
|
||||||
|
ChangeTerritory((ETerritoryType)territoryType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ChangeTerritory(ETerritoryType newTerritory)
|
||||||
|
{
|
||||||
|
var territory = _territories[newTerritory];
|
||||||
|
if (!territory.IsReady && !territory.IsLoading)
|
||||||
|
{
|
||||||
|
territory.IsLoading = true;
|
||||||
|
new LoadTerritory(_serviceScopeFactory, territory).Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemoryTerritory? GetTerritoryIfReady(ushort territoryType)
|
||||||
|
{
|
||||||
|
if (typeof(ETerritoryType).IsEnumDefined(territoryType))
|
||||||
|
return GetTerritoryIfReady((ETerritoryType)territoryType);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemoryTerritory? GetTerritoryIfReady(ETerritoryType territoryType)
|
||||||
|
{
|
||||||
|
var territory = _territories[territoryType];
|
||||||
|
if (!territory.IsReady)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return territory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsReady(ushort territoryId) => GetTerritoryIfReady(territoryId) != null;
|
||||||
|
|
||||||
|
public bool MergePersistentLocations(
|
||||||
|
ETerritoryType territoryType,
|
||||||
|
IReadOnlyList<PersistentLocation> visibleLocations,
|
||||||
|
bool recreateLayout,
|
||||||
|
out List<PersistentLocation> locationsToSync)
|
||||||
|
{
|
||||||
|
MemoryTerritory? territory = GetTerritoryIfReady(territoryType);
|
||||||
|
locationsToSync = new();
|
||||||
|
if (territory == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var partialAccountId = _configuration.FindAccount(RemoteApi.RemoteUrl)?.AccountId.ToPartialId();
|
||||||
|
var persistentLocations = territory.Locations.ToList();
|
||||||
|
|
||||||
|
List<PersistentLocation> markAsSeen = new();
|
||||||
|
List<PersistentLocation> newLocations = new();
|
||||||
|
foreach (var visibleLocation in visibleLocations)
|
||||||
|
{
|
||||||
|
PersistentLocation? existingLocation = persistentLocations.SingleOrDefault(x => x == visibleLocation);
|
||||||
|
if (existingLocation != null)
|
||||||
|
{
|
||||||
|
if (existingLocation is { Seen: false, LocalId: { } })
|
||||||
|
{
|
||||||
|
existingLocation.Seen = true;
|
||||||
|
markAsSeen.Add(existingLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This requires you to have seen a trap/hoard marker once per floor to synchronize this for older local states,
|
||||||
|
// markers discovered afterwards are automatically marked seen.
|
||||||
|
if (partialAccountId != null &&
|
||||||
|
existingLocation is { LocalId: { }, NetworkId: { }, RemoteSeenRequested: false } &&
|
||||||
|
!existingLocation.RemoteSeenOn.Contains(partialAccountId))
|
||||||
|
{
|
||||||
|
existingLocation.RemoteSeenRequested = true;
|
||||||
|
locationsToSync.Add(existingLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
territory.Locations.Add(visibleLocation);
|
||||||
|
newLocations.Add(visibleLocation);
|
||||||
|
recreateLayout = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (markAsSeen.Count > 0)
|
||||||
|
new MarkAsSeen(_serviceScopeFactory, territory, markAsSeen).Start();
|
||||||
|
|
||||||
|
if (newLocations.Count > 0)
|
||||||
|
new SaveNewLocations(_serviceScopeFactory, territory, newLocations).Start();
|
||||||
|
|
||||||
|
return recreateLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>Whether the locations have changed</returns>
|
||||||
|
public bool MergeEphemeralLocations(IReadOnlyList<EphemeralLocation> visibleLocations, bool recreate)
|
||||||
|
{
|
||||||
|
recreate |= _ephemeralLocations.Any(loc => visibleLocations.All(x => x != loc));
|
||||||
|
recreate |= visibleLocations.Any(loc => _ephemeralLocations.All(x => x != loc));
|
||||||
|
|
||||||
|
if (!recreate)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_ephemeralLocations.Clear();
|
||||||
|
foreach (var visibleLocation in visibleLocations)
|
||||||
|
_ephemeralLocations.Add(visibleLocation);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetAll()
|
||||||
|
{
|
||||||
|
foreach (var memoryTerritory in _territories.Values)
|
||||||
|
{
|
||||||
|
lock (memoryTerritory.LockObj)
|
||||||
|
memoryTerritory.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
Pal.Client/Floors/MemoryLocation.cs
Normal file
56
Pal.Client/Floors/MemoryLocation.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Numerics;
|
||||||
|
using Pal.Client.Rendering;
|
||||||
|
using Pal.Common;
|
||||||
|
using Palace;
|
||||||
|
|
||||||
|
namespace Pal.Client.Floors
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for <see cref="MemoryLocation"/> and <see cref="EphemeralLocation"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal abstract class MemoryLocation
|
||||||
|
{
|
||||||
|
public required EType Type { get; init; }
|
||||||
|
public required Vector3 Position { get; init; }
|
||||||
|
public bool Seen { get; set; }
|
||||||
|
|
||||||
|
public IRenderElement? RenderElement { get; set; }
|
||||||
|
|
||||||
|
public enum EType
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
|
||||||
|
Hoard,
|
||||||
|
Trap,
|
||||||
|
|
||||||
|
SilverCoffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
return obj is MemoryLocation otherLocation &&
|
||||||
|
Type == otherLocation.Type &&
|
||||||
|
PalaceMath.IsNearlySamePosition(Position, otherLocation.Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(Type, PalaceMath.GetHashCode(Position));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class ETypeExtensions
|
||||||
|
{
|
||||||
|
public static MemoryLocation.EType ToMemoryType(this ObjectType objectType)
|
||||||
|
{
|
||||||
|
return objectType switch
|
||||||
|
{
|
||||||
|
ObjectType.Trap => MemoryLocation.EType.Trap,
|
||||||
|
ObjectType.Hoard => MemoryLocation.EType.Hoard,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(objectType), objectType, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
Pal.Client/Floors/MemoryTerritory.cs
Normal file
49
Pal.Client/Floors/MemoryTerritory.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Pal.Client.Configuration;
|
||||||
|
using Pal.Common;
|
||||||
|
|
||||||
|
namespace Pal.Client.Floors
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A single set of floors loaded entirely in memory, can be e.g. POTD 51-60.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class MemoryTerritory
|
||||||
|
{
|
||||||
|
public MemoryTerritory(ETerritoryType territoryType)
|
||||||
|
{
|
||||||
|
TerritoryType = territoryType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ETerritoryType TerritoryType { get; }
|
||||||
|
public bool IsReady { get; set; }
|
||||||
|
public bool IsLoading { get; set; }
|
||||||
|
|
||||||
|
public ConcurrentBag<PersistentLocation> Locations { get; } = new();
|
||||||
|
public object LockObj { get; } = new();
|
||||||
|
|
||||||
|
public void Initialize(IEnumerable<PersistentLocation> locations)
|
||||||
|
{
|
||||||
|
Locations.Clear();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
Locations.Clear();
|
||||||
|
IsReady = false;
|
||||||
|
IsLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
Pal.Client/Floors/PersistentLocation.cs
Normal file
48
Pal.Client/Floors/PersistentLocation.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Pal.Client.Database;
|
||||||
|
|
||||||
|
namespace Pal.Client.Floors
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="ClientLocation"/> loaded in memory, with certain extra attributes as needed.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class PersistentLocation : MemoryLocation
|
||||||
|
{
|
||||||
|
/// <see cref="ClientLocation.LocalId"/>
|
||||||
|
public int? LocalId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Network id for the server you're currently connected to.
|
||||||
|
/// </summary>
|
||||||
|
public Guid? NetworkId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For markers that the server you're connected to doesn't know: Whether this was requested to be uploaded, to avoid duplicate requests.
|
||||||
|
/// </summary>
|
||||||
|
public bool UploadRequested { get; set; }
|
||||||
|
|
||||||
|
/// <see cref="ClientLocation.RemoteEncounters"/>
|
||||||
|
///
|
||||||
|
public List<string> RemoteSeenOn { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this marker was requested to be seen, to avoid duplicate requests.
|
||||||
|
/// </summary>
|
||||||
|
public bool RemoteSeenRequested { get; set; }
|
||||||
|
|
||||||
|
public override bool Equals(object? obj) => obj is PersistentLocation && base.Equals(obj);
|
||||||
|
|
||||||
|
public override int GetHashCode() => base.GetHashCode();
|
||||||
|
|
||||||
|
public static bool operator ==(PersistentLocation? a, object? b)
|
||||||
|
{
|
||||||
|
return Equals(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool operator !=(PersistentLocation? a, object? b)
|
||||||
|
{
|
||||||
|
return !Equals(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
Pal.Client/Floors/Tasks/DbTask.cs
Normal file
29
Pal.Client/Floors/Tasks/DbTask.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Pal.Client.Database;
|
||||||
|
|
||||||
|
namespace Pal.Client.Floors.Tasks
|
||||||
|
{
|
||||||
|
internal abstract class DbTask
|
||||||
|
{
|
||||||
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||||
|
|
||||||
|
protected DbTask(IServiceScopeFactory serviceScopeFactory)
|
||||||
|
{
|
||||||
|
_serviceScopeFactory = serviceScopeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
|
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||||
|
|
||||||
|
Run(dbContext);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void Run(PalClientContext dbContext);
|
||||||
|
}
|
||||||
|
}
|
59
Pal.Client/Floors/Tasks/LoadTerritory.cs
Normal file
59
Pal.Client/Floors/Tasks/LoadTerritory.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Pal.Client.Database;
|
||||||
|
|
||||||
|
namespace Pal.Client.Floors.Tasks
|
||||||
|
{
|
||||||
|
internal sealed class LoadTerritory : DbTask
|
||||||
|
{
|
||||||
|
private readonly MemoryTerritory _territory;
|
||||||
|
|
||||||
|
public LoadTerritory(IServiceScopeFactory serviceScopeFactory, MemoryTerritory territory)
|
||||||
|
: base(serviceScopeFactory)
|
||||||
|
{
|
||||||
|
_territory = territory;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Run(PalClientContext dbContext)
|
||||||
|
{
|
||||||
|
lock (_territory.LockObj)
|
||||||
|
{
|
||||||
|
if (_territory.IsReady)
|
||||||
|
return;
|
||||||
|
|
||||||
|
List<ClientLocation> locations = dbContext.Locations
|
||||||
|
.Where(o => o.TerritoryType == (ushort)_territory.TerritoryType)
|
||||||
|
.Include(o => o.ImportedBy)
|
||||||
|
.Include(o => o.RemoteEncounters)
|
||||||
|
.ToList();
|
||||||
|
_territory.Initialize(locations.Select(ToMemoryLocation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PersistentLocation ToMemoryLocation(ClientLocation location)
|
||||||
|
{
|
||||||
|
return new PersistentLocation
|
||||||
|
{
|
||||||
|
LocalId = location.LocalId,
|
||||||
|
Type = ToMemoryLocationType(location.Type),
|
||||||
|
Position = new Vector3(location.X, location.Y, location.Z),
|
||||||
|
Seen = location.Seen,
|
||||||
|
RemoteSeenOn = location.RemoteEncounters.Select(o => o.AccountId).ToList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MemoryLocation.EType ToMemoryLocationType(ClientLocation.EType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
ClientLocation.EType.Trap => MemoryLocation.EType.Trap,
|
||||||
|
ClientLocation.EType.Hoard => MemoryLocation.EType.Hoard,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
Pal.Client/Floors/Tasks/MarkAsSeen.cs
Normal file
33
Pal.Client/Floors/Tasks/MarkAsSeen.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Pal.Client.Database;
|
||||||
|
|
||||||
|
namespace Pal.Client.Floors.Tasks
|
||||||
|
{
|
||||||
|
internal sealed class MarkAsSeen : DbTask
|
||||||
|
{
|
||||||
|
private readonly MemoryTerritory _territory;
|
||||||
|
private readonly IReadOnlyList<PersistentLocation> _locations;
|
||||||
|
|
||||||
|
public MarkAsSeen(IServiceScopeFactory serviceScopeFactory, MemoryTerritory territory,
|
||||||
|
IReadOnlyList<PersistentLocation> locations)
|
||||||
|
: base(serviceScopeFactory)
|
||||||
|
{
|
||||||
|
_territory = territory;
|
||||||
|
_locations = locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Run(PalClientContext dbContext)
|
||||||
|
{
|
||||||
|
lock (_territory.LockObj)
|
||||||
|
{
|
||||||
|
dbContext.Locations
|
||||||
|
.Where(loc => _locations.Any(l => l.LocalId == loc.LocalId))
|
||||||
|
.ExecuteUpdate(loc => loc.SetProperty(l => l.Seen, true));
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
Pal.Client/Floors/Tasks/MarkRemoteSeen.cs
Normal file
39
Pal.Client/Floors/Tasks/MarkRemoteSeen.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Pal.Client.Database;
|
||||||
|
|
||||||
|
namespace Pal.Client.Floors.Tasks
|
||||||
|
{
|
||||||
|
internal sealed class MarkRemoteSeen : DbTask
|
||||||
|
{
|
||||||
|
private readonly MemoryTerritory _territory;
|
||||||
|
private readonly IReadOnlyList<PersistentLocation> _locations;
|
||||||
|
private readonly string _accountId;
|
||||||
|
|
||||||
|
public MarkRemoteSeen(IServiceScopeFactory serviceScopeFactory,
|
||||||
|
MemoryTerritory territory,
|
||||||
|
IReadOnlyList<PersistentLocation> locations,
|
||||||
|
string accountId)
|
||||||
|
: base(serviceScopeFactory)
|
||||||
|
{
|
||||||
|
_territory = territory;
|
||||||
|
_locations = locations;
|
||||||
|
_accountId = accountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Run(PalClientContext dbContext)
|
||||||
|
{
|
||||||
|
lock (_territory.LockObj)
|
||||||
|
{
|
||||||
|
List<ClientLocation> locationsToUpdate = dbContext.Locations
|
||||||
|
.Where(loc => _locations.Any(l =>
|
||||||
|
l.LocalId == loc.LocalId && loc.RemoteEncounters.All(r => r.AccountId != _accountId)))
|
||||||
|
.ToList();
|
||||||
|
foreach (var clientLocation in locationsToUpdate)
|
||||||
|
clientLocation.RemoteEncounters.Add(new RemoteEncounter(clientLocation, _accountId));
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
Pal.Client/Floors/Tasks/SaveNewLocations.cs
Normal file
69
Pal.Client/Floors/Tasks/SaveNewLocations.cs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Pal.Client.Database;
|
||||||
|
using Pal.Common;
|
||||||
|
|
||||||
|
namespace Pal.Client.Floors.Tasks
|
||||||
|
{
|
||||||
|
internal sealed class SaveNewLocations : DbTask
|
||||||
|
{
|
||||||
|
private readonly MemoryTerritory _territory;
|
||||||
|
private readonly List<PersistentLocation> _newLocations;
|
||||||
|
|
||||||
|
public SaveNewLocations(IServiceScopeFactory serviceScopeFactory, MemoryTerritory territory,
|
||||||
|
List<PersistentLocation> newLocations)
|
||||||
|
: base(serviceScopeFactory)
|
||||||
|
{
|
||||||
|
_territory = territory;
|
||||||
|
_newLocations = newLocations;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Run(PalClientContext dbContext)
|
||||||
|
{
|
||||||
|
Run(_territory, dbContext, _newLocations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Run(MemoryTerritory territory, PalClientContext dbContext,
|
||||||
|
List<PersistentLocation> locations)
|
||||||
|
{
|
||||||
|
lock (territory.LockObj)
|
||||||
|
{
|
||||||
|
Dictionary<PersistentLocation, ClientLocation> mapping =
|
||||||
|
locations.ToDictionary(x => x, x => ToDatabaseLocation(x, territory.TerritoryType));
|
||||||
|
dbContext.Locations.AddRange(mapping.Values);
|
||||||
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
foreach ((PersistentLocation persistentLocation, ClientLocation clientLocation) in mapping)
|
||||||
|
{
|
||||||
|
persistentLocation.LocalId = clientLocation.LocalId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ClientLocation ToDatabaseLocation(PersistentLocation location, ETerritoryType territoryType)
|
||||||
|
{
|
||||||
|
return new ClientLocation
|
||||||
|
{
|
||||||
|
TerritoryType = (ushort)territoryType,
|
||||||
|
Type = ToDatabaseType(location.Type),
|
||||||
|
X = location.Position.X,
|
||||||
|
Y = location.Position.Y,
|
||||||
|
Z = location.Position.Z,
|
||||||
|
Seen = location.Seen,
|
||||||
|
SinceVersion = typeof(Plugin).Assembly.GetName().Version!.ToString(2),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ClientLocation.EType ToDatabaseType(MemoryLocation.EType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
MemoryLocation.EType.Trap => ClientLocation.EType.Trap,
|
||||||
|
MemoryLocation.EType.Hoard => ClientLocation.EType.Hoard,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,159 +0,0 @@
|
|||||||
using Pal.Common;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text.Json;
|
|
||||||
using Pal.Client.Configuration;
|
|
||||||
using Pal.Client.Extensions;
|
|
||||||
|
|
||||||
namespace Pal.Client
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// JSON for a single floor set (e.g. 51-60).
|
|
||||||
/// </summary>
|
|
||||||
internal sealed class LocalState
|
|
||||||
{
|
|
||||||
private static readonly JsonSerializerOptions JsonSerializerOptions = new() { IncludeFields = true };
|
|
||||||
private const int CurrentVersion = 4;
|
|
||||||
|
|
||||||
internal static string PluginConfigDirectory { get; set; } = null!;
|
|
||||||
internal static EMode Mode { get; set; }
|
|
||||||
|
|
||||||
public uint TerritoryType { get; set; }
|
|
||||||
public ConcurrentBag<Marker> Markers { get; set; } = new();
|
|
||||||
|
|
||||||
public LocalState(uint territoryType)
|
|
||||||
{
|
|
||||||
TerritoryType = territoryType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyFilters()
|
|
||||||
{
|
|
||||||
if (Mode == EMode.Offline)
|
|
||||||
Markers = new ConcurrentBag<Marker>(Markers.Where(x => x.Seen || (x.WasImported && x.Imports.Count > 0)));
|
|
||||||
else
|
|
||||||
// ensure old import markers are removed if they are no longer part of a "current" import
|
|
||||||
// this MAY remove markers the server sent you (and that you haven't seen), but this should be fixed the next time you enter the zone
|
|
||||||
Markers = new ConcurrentBag<Marker>(Markers.Where(x => x.Seen || !x.WasImported || x.Imports.Count > 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static LocalState? Load(uint territoryType)
|
|
||||||
{
|
|
||||||
string path = GetSaveLocation(territoryType);
|
|
||||||
if (!File.Exists(path))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
string content = File.ReadAllText(path);
|
|
||||||
if (content.Length == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
LocalState localState;
|
|
||||||
int version = 1;
|
|
||||||
if (content[0] == '[')
|
|
||||||
{
|
|
||||||
// v1 only had a list of markers, not a JSON object as root
|
|
||||||
localState = new LocalState(territoryType)
|
|
||||||
{
|
|
||||||
Markers = new ConcurrentBag<Marker>(JsonSerializer.Deserialize<HashSet<Marker>>(content, JsonSerializerOptions) ?? new()),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var save = JsonSerializer.Deserialize<SaveFile>(content, JsonSerializerOptions);
|
|
||||||
if (save == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
localState = new LocalState(territoryType)
|
|
||||||
{
|
|
||||||
Markers = new ConcurrentBag<Marker>(save.Markers.Where(o => o.Type == Marker.EType.Trap || o.Type == Marker.EType.Hoard)),
|
|
||||||
};
|
|
||||||
version = save.Version;
|
|
||||||
}
|
|
||||||
|
|
||||||
localState.ApplyFilters();
|
|
||||||
|
|
||||||
if (version <= 3)
|
|
||||||
{
|
|
||||||
foreach (var marker in localState.Markers)
|
|
||||||
marker.RemoteSeenOn = marker.RemoteSeenOn.Select(x => x.ToPartialId()).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version < CurrentVersion)
|
|
||||||
localState.Save();
|
|
||||||
|
|
||||||
return localState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
string path = GetSaveLocation(TerritoryType);
|
|
||||||
|
|
||||||
ApplyFilters();
|
|
||||||
SaveImpl(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Backup(string suffix)
|
|
||||||
{
|
|
||||||
string path = $"{GetSaveLocation(TerritoryType)}.{suffix}";
|
|
||||||
if (!File.Exists(path))
|
|
||||||
{
|
|
||||||
SaveImpl(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SaveImpl(string path)
|
|
||||||
{
|
|
||||||
foreach (var marker in Markers)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(marker.SinceVersion))
|
|
||||||
marker.SinceVersion = typeof(Plugin).Assembly.GetName().Version!.ToString(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Markers.Count == 0)
|
|
||||||
File.Delete(path);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
File.WriteAllText(path, JsonSerializer.Serialize(new SaveFile
|
|
||||||
{
|
|
||||||
Version = CurrentVersion,
|
|
||||||
Markers = new HashSet<Marker>(Markers)
|
|
||||||
}, JsonSerializerOptions));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetSaveLocation() => GetSaveLocation(TerritoryType);
|
|
||||||
|
|
||||||
private static string GetSaveLocation(uint territoryType) => Path.Join(PluginConfigDirectory, $"{territoryType}.json");
|
|
||||||
|
|
||||||
public static void ForEach(Action<LocalState> action)
|
|
||||||
{
|
|
||||||
foreach (ETerritoryType territory in typeof(ETerritoryType).GetEnumValues())
|
|
||||||
{
|
|
||||||
LocalState? localState = Load((ushort)territory);
|
|
||||||
if (localState != null)
|
|
||||||
action(localState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void UpdateAll()
|
|
||||||
{
|
|
||||||
ForEach(s => s.Save());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UndoImport(List<Guid> importIds)
|
|
||||||
{
|
|
||||||
// When saving a floor state, any markers not seen, not remote seen, and not having an import id are removed;
|
|
||||||
// so it is possible to remove "wrong" markers by not having them be in the current import.
|
|
||||||
foreach (var marker in Markers)
|
|
||||||
marker.Imports.RemoveAll(importIds.Contains);
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class SaveFile
|
|
||||||
{
|
|
||||||
public int Version { get; set; }
|
|
||||||
public HashSet<Marker> Markers { get; set; } = new();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,110 +0,0 @@
|
|||||||
using ECommons.SplatoonAPI;
|
|
||||||
using Pal.Client.Rendering;
|
|
||||||
using Pal.Common;
|
|
||||||
using Palace;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Pal.Client
|
|
||||||
{
|
|
||||||
internal sealed class Marker
|
|
||||||
{
|
|
||||||
public EType Type { get; set; } = EType.Unknown;
|
|
||||||
public Vector3 Position { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether we have encountered the trap/coffer at this location in-game.
|
|
||||||
/// </summary>
|
|
||||||
public bool Seen { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Network id for the server you're currently connected to.
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
public Guid? NetworkId { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// For markers that the server you're connected to doesn't know: Whether this was requested to be uploaded, to avoid duplicate requests.
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
public bool UploadRequested { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Which account ids this marker was seen. This is a list merely to support different remote endpoints
|
|
||||||
/// (where each server would assign you a different id).
|
|
||||||
/// </summary>
|
|
||||||
public List<string> RemoteSeenOn { get; set; } = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether this marker was requested to be seen, to avoid duplicate requests.
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
public bool RemoteSeenRequested { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// To keep track of which markers were imported through a downloaded file, we save the associated import-id.
|
|
||||||
///
|
|
||||||
/// Importing another file for the same remote server will remove the old import-id, and add the new import-id here.
|
|
||||||
/// </summary>
|
|
||||||
public List<Guid> Imports { get; set; } = new();
|
|
||||||
|
|
||||||
public bool WasImported { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// To make rollbacks of local data easier, keep track of the version which was used to write the marker initially.
|
|
||||||
/// </summary>
|
|
||||||
public string? SinceVersion { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public IRenderElement? RenderElement { get; set; }
|
|
||||||
|
|
||||||
public Marker(EType type, Vector3 position, Guid? networkId = null)
|
|
||||||
{
|
|
||||||
Type = type;
|
|
||||||
Position = position;
|
|
||||||
NetworkId = networkId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return HashCode.Combine(Type, PalaceMath.GetHashCode(Position));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
|
||||||
{
|
|
||||||
return obj is Marker otherMarker && Type == otherMarker.Type && PalaceMath.IsNearlySamePosition(Position, otherMarker.Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(Marker? a, object? b)
|
|
||||||
{
|
|
||||||
return Equals(a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(Marker? a, object? b)
|
|
||||||
{
|
|
||||||
return !Equals(a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public bool IsPermanent() => Type == EType.Trap || Type == EType.Hoard;
|
|
||||||
|
|
||||||
public enum EType
|
|
||||||
{
|
|
||||||
Unknown = ObjectType.Unknown,
|
|
||||||
|
|
||||||
#region Permanent Markers
|
|
||||||
Trap = ObjectType.Trap,
|
|
||||||
Hoard = ObjectType.Hoard,
|
|
||||||
|
|
||||||
[Obsolete]
|
|
||||||
Debug = 3,
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
# region Markers that only show up if they're currently visible
|
|
||||||
SilverCoffer = 100,
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,24 +5,25 @@ 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.Floors;
|
||||||
|
|
||||||
namespace Pal.Client.Net
|
namespace Pal.Client.Net
|
||||||
{
|
{
|
||||||
internal partial class RemoteApi
|
internal partial class RemoteApi
|
||||||
{
|
{
|
||||||
public async Task<(bool, List<Marker>)> DownloadRemoteMarkers(ushort territoryId, CancellationToken cancellationToken = default)
|
public async Task<(bool, List<PersistentLocation>)> DownloadRemoteMarkers(ushort territoryId, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (!await Connect(cancellationToken))
|
if (!await Connect(cancellationToken))
|
||||||
return (false, new());
|
return (false, new());
|
||||||
|
|
||||||
var palaceClient = new PalaceService.PalaceServiceClient(_channel);
|
var palaceClient = new PalaceService.PalaceServiceClient(_channel);
|
||||||
var downloadReply = await palaceClient.DownloadFloorsAsync(new DownloadFloorsRequest { TerritoryType = territoryId }, headers: AuthorizedHeaders(), cancellationToken: cancellationToken);
|
var downloadReply = await palaceClient.DownloadFloorsAsync(new DownloadFloorsRequest { TerritoryType = territoryId }, headers: AuthorizedHeaders(), cancellationToken: cancellationToken);
|
||||||
return (downloadReply.Success, downloadReply.Objects.Select(CreateMarkerFromNetworkObject).ToList());
|
return (downloadReply.Success, downloadReply.Objects.Select(CreateLocationFromNetworkObject).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(bool, List<Marker>)> UploadMarker(ushort territoryType, IList<Marker> markers, CancellationToken cancellationToken = default)
|
public async Task<(bool, List<PersistentLocation>)> UploadLocations(ushort territoryType, IReadOnlyList<PersistentLocation> locations, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (markers.Count == 0)
|
if (locations.Count == 0)
|
||||||
return (true, new());
|
return (true, new());
|
||||||
|
|
||||||
if (!await Connect(cancellationToken))
|
if (!await Connect(cancellationToken))
|
||||||
@ -33,7 +34,7 @@ namespace Pal.Client.Net
|
|||||||
{
|
{
|
||||||
TerritoryType = territoryType,
|
TerritoryType = territoryType,
|
||||||
};
|
};
|
||||||
uploadRequest.Objects.AddRange(markers.Select(m => new PalaceObject
|
uploadRequest.Objects.AddRange(locations.Select(m => new PalaceObject
|
||||||
{
|
{
|
||||||
Type = (ObjectType)m.Type,
|
Type = (ObjectType)m.Type,
|
||||||
X = m.Position.X,
|
X = m.Position.X,
|
||||||
@ -41,12 +42,12 @@ namespace Pal.Client.Net
|
|||||||
Z = m.Position.Z
|
Z = m.Position.Z
|
||||||
}));
|
}));
|
||||||
var uploadReply = await palaceClient.UploadFloorsAsync(uploadRequest, headers: AuthorizedHeaders(), cancellationToken: cancellationToken);
|
var uploadReply = await palaceClient.UploadFloorsAsync(uploadRequest, headers: AuthorizedHeaders(), cancellationToken: cancellationToken);
|
||||||
return (uploadReply.Success, uploadReply.Objects.Select(CreateMarkerFromNetworkObject).ToList());
|
return (uploadReply.Success, uploadReply.Objects.Select(CreateLocationFromNetworkObject).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> MarkAsSeen(ushort territoryType, IList<Marker> markers, CancellationToken cancellationToken = default)
|
public async Task<bool> MarkAsSeen(ushort territoryType, IReadOnlyList<PersistentLocation> locations, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (markers.Count == 0)
|
if (locations.Count == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!await Connect(cancellationToken))
|
if (!await Connect(cancellationToken))
|
||||||
@ -54,15 +55,22 @@ namespace Pal.Client.Net
|
|||||||
|
|
||||||
var palaceClient = new PalaceService.PalaceServiceClient(_channel);
|
var palaceClient = new PalaceService.PalaceServiceClient(_channel);
|
||||||
var seenRequest = new MarkObjectsSeenRequest { TerritoryType = territoryType };
|
var seenRequest = new MarkObjectsSeenRequest { TerritoryType = territoryType };
|
||||||
foreach (var marker in markers)
|
foreach (var marker in locations)
|
||||||
seenRequest.NetworkIds.Add(marker.NetworkId.ToString());
|
seenRequest.NetworkIds.Add(marker.NetworkId.ToString());
|
||||||
|
|
||||||
var seenReply = await palaceClient.MarkObjectsSeenAsync(seenRequest, headers: AuthorizedHeaders(), deadline: DateTime.UtcNow.AddSeconds(10), cancellationToken: cancellationToken);
|
var seenReply = await palaceClient.MarkObjectsSeenAsync(seenRequest, headers: AuthorizedHeaders(), deadline: DateTime.UtcNow.AddSeconds(10), cancellationToken: cancellationToken);
|
||||||
return seenReply.Success;
|
return seenReply.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Marker CreateMarkerFromNetworkObject(PalaceObject obj) =>
|
private PersistentLocation CreateLocationFromNetworkObject(PalaceObject obj)
|
||||||
new Marker((Marker.EType)obj.Type, new Vector3(obj.X, obj.Y, obj.Z), Guid.Parse(obj.NetworkId));
|
{
|
||||||
|
return new PersistentLocation
|
||||||
|
{
|
||||||
|
Type = obj.Type.ToMemoryType(),
|
||||||
|
Position = new Vector3(obj.X, obj.Y, obj.Z),
|
||||||
|
NetworkId = Guid.Parse(obj.NetworkId),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<(bool, List<FloorStatistics>)> FetchStatistics(CancellationToken cancellationToken = default)
|
public async Task<(bool, List<FloorStatistics>)> FetchStatistics(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,7 @@ namespace Pal.Client.Net
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
public const string RemoteUrl = "http://localhost:5145";
|
public const string RemoteUrl = "http://localhost:5145";
|
||||||
#else
|
#else
|
||||||
public const string RemoteUrl = "https://pal.liza.sh";
|
//public const string RemoteUrl = "https://pal.liza.sh";
|
||||||
#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)}";
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Pal.Client.Configuration;
|
using Pal.Client.Configuration;
|
||||||
|
using Pal.Client.Floors;
|
||||||
|
|
||||||
namespace Pal.Client.Rendering
|
namespace Pal.Client.Rendering
|
||||||
{
|
{
|
||||||
@ -12,7 +13,7 @@ namespace Pal.Client.Rendering
|
|||||||
|
|
||||||
void ResetLayer(ELayer layer);
|
void ResetLayer(ELayer layer);
|
||||||
|
|
||||||
IRenderElement CreateElement(Marker.EType type, Vector3 pos, uint color, bool fill = false);
|
IRenderElement CreateElement(MemoryLocation.EType type, Vector3 pos, uint color, bool fill = false);
|
||||||
|
|
||||||
void DrawDebugItems(uint trapColor, uint hoardColor);
|
void DrawDebugItems(uint trapColor, uint hoardColor);
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Pal.Client.Floors;
|
||||||
|
|
||||||
namespace Pal.Client.Rendering
|
namespace Pal.Client.Rendering
|
||||||
{
|
{
|
||||||
internal sealed class MarkerConfig
|
internal sealed class MarkerConfig
|
||||||
{
|
{
|
||||||
private static readonly MarkerConfig EmptyConfig = new();
|
private static readonly MarkerConfig EmptyConfig = new();
|
||||||
private static readonly Dictionary<Marker.EType, MarkerConfig> MarkerConfigs = new()
|
|
||||||
|
private static readonly Dictionary<MemoryLocation.EType, MarkerConfig> MarkerConfigs = new()
|
||||||
{
|
{
|
||||||
{ Marker.EType.Trap, new MarkerConfig { Radius = 1.7f } },
|
{ MemoryLocation.EType.Trap, new MarkerConfig { Radius = 1.7f } },
|
||||||
{ Marker.EType.Hoard, new MarkerConfig { Radius = 1.7f, OffsetY = -0.03f } },
|
{ MemoryLocation.EType.Hoard, new MarkerConfig { Radius = 1.7f, OffsetY = -0.03f } },
|
||||||
{ Marker.EType.SilverCoffer, new MarkerConfig { Radius = 1f } },
|
{ MemoryLocation.EType.SilverCoffer, new MarkerConfig { Radius = 1f } },
|
||||||
};
|
};
|
||||||
|
|
||||||
public float OffsetY { get; private init; }
|
public float OffsetY { get; private init; }
|
||||||
public float Radius { get; private init; } = 0.25f;
|
public float Radius { get; private init; } = 0.25f;
|
||||||
|
|
||||||
public static MarkerConfig ForType(Marker.EType type) => MarkerConfigs.GetValueOrDefault(type, EmptyConfig);
|
public static MarkerConfig ForType(MemoryLocation.EType type) =>
|
||||||
|
MarkerConfigs.GetValueOrDefault(type, EmptyConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ using System.Numerics;
|
|||||||
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.Floors;
|
||||||
|
|
||||||
namespace Pal.Client.Rendering
|
namespace Pal.Client.Rendering
|
||||||
{
|
{
|
||||||
@ -56,7 +57,7 @@ namespace Pal.Client.Rendering
|
|||||||
public void ResetLayer(ELayer layer)
|
public void ResetLayer(ELayer layer)
|
||||||
=> _implementation.ResetLayer(layer);
|
=> _implementation.ResetLayer(layer);
|
||||||
|
|
||||||
public IRenderElement CreateElement(Marker.EType type, Vector3 pos, uint color, bool fill = false)
|
public IRenderElement CreateElement(MemoryLocation.EType type, Vector3 pos, uint color, bool fill = false)
|
||||||
=> _implementation.CreateElement(type, pos, color, fill);
|
=> _implementation.CreateElement(type, pos, color, fill);
|
||||||
|
|
||||||
public ERenderer GetConfigValue()
|
public ERenderer GetConfigValue()
|
||||||
|
@ -9,6 +9,7 @@ using Dalamud.Game.ClientState;
|
|||||||
using Dalamud.Game.Gui;
|
using Dalamud.Game.Gui;
|
||||||
using Pal.Client.Configuration;
|
using Pal.Client.Configuration;
|
||||||
using Pal.Client.DependencyInjection;
|
using Pal.Client.DependencyInjection;
|
||||||
|
using Pal.Client.Floors;
|
||||||
|
|
||||||
namespace Pal.Client.Rendering
|
namespace Pal.Client.Rendering
|
||||||
{
|
{
|
||||||
@ -53,7 +54,7 @@ namespace Pal.Client.Rendering
|
|||||||
l.Dispose();
|
l.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IRenderElement CreateElement(Marker.EType type, Vector3 pos, uint color, bool fill = false)
|
public IRenderElement CreateElement(MemoryLocation.EType type, Vector3 pos, uint color, bool fill = false)
|
||||||
{
|
{
|
||||||
var config = MarkerConfig.ForType(type);
|
var config = MarkerConfig.ForType(type);
|
||||||
return new SimpleElement
|
return new SimpleElement
|
||||||
@ -73,9 +74,13 @@ namespace Pal.Client.Rendering
|
|||||||
TerritoryType = _clientState.TerritoryType,
|
TerritoryType = _clientState.TerritoryType,
|
||||||
Elements = new List<SimpleElement>
|
Elements = new List<SimpleElement>
|
||||||
{
|
{
|
||||||
(SimpleElement)CreateElement(Marker.EType.Trap, _clientState.LocalPlayer?.Position ?? default,
|
(SimpleElement)CreateElement(
|
||||||
|
MemoryLocation.EType.Trap,
|
||||||
|
_clientState.LocalPlayer?.Position ?? default,
|
||||||
trapColor),
|
trapColor),
|
||||||
(SimpleElement)CreateElement(Marker.EType.Hoard, _clientState.LocalPlayer?.Position ?? default,
|
(SimpleElement)CreateElement(
|
||||||
|
MemoryLocation.EType.Hoard,
|
||||||
|
_clientState.LocalPlayer?.Position ?? default,
|
||||||
hoardColor)
|
hoardColor)
|
||||||
},
|
},
|
||||||
ExpiresAt = Environment.TickCount64 + RenderData.TestLayerTimeout
|
ExpiresAt = Environment.TickCount64 + RenderData.TestLayerTimeout
|
||||||
@ -120,15 +125,15 @@ namespace Pal.Client.Rendering
|
|||||||
|
|
||||||
switch (e.Type)
|
switch (e.Type)
|
||||||
{
|
{
|
||||||
case Marker.EType.Hoard:
|
case MemoryLocation.EType.Hoard:
|
||||||
// ignore distance if this is a found hoard coffer
|
// ignore distance if this is a found hoard coffer
|
||||||
if (_territoryState.PomanderOfIntuition == PomanderState.Active &&
|
if (_territoryState.PomanderOfIntuition == PomanderState.Active &&
|
||||||
_configuration.DeepDungeons.HoardCoffers.OnlyVisibleAfterPomander)
|
_configuration.DeepDungeons.HoardCoffers.OnlyVisibleAfterPomander)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
goto case Marker.EType.Trap;
|
goto case MemoryLocation.EType.Trap;
|
||||||
|
|
||||||
case Marker.EType.Trap:
|
case MemoryLocation.EType.Trap:
|
||||||
var playerPos = _clientState.LocalPlayer?.Position;
|
var playerPos = _clientState.LocalPlayer?.Position;
|
||||||
if (playerPos == null)
|
if (playerPos == null)
|
||||||
return;
|
return;
|
||||||
@ -189,7 +194,7 @@ namespace Pal.Client.Rendering
|
|||||||
public sealed class SimpleElement : IRenderElement
|
public sealed class SimpleElement : IRenderElement
|
||||||
{
|
{
|
||||||
public bool IsValid { get; set; } = true;
|
public bool IsValid { get; set; } = true;
|
||||||
public required Marker.EType Type { get; init; }
|
public required MemoryLocation.EType Type { get; init; }
|
||||||
public required Vector3 Position { get; init; }
|
public required Vector3 Position { get; init; }
|
||||||
public required uint Color { get; set; }
|
public required uint Color { get; set; }
|
||||||
public required float Radius { get; init; }
|
public required float Radius { get; init; }
|
||||||
|
@ -13,6 +13,7 @@ using Dalamud.Game.ClientState;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Pal.Client.Configuration;
|
using Pal.Client.Configuration;
|
||||||
using Pal.Client.DependencyInjection;
|
using Pal.Client.DependencyInjection;
|
||||||
|
using Pal.Client.Floors;
|
||||||
|
|
||||||
namespace Pal.Client.Rendering
|
namespace Pal.Client.Rendering
|
||||||
{
|
{
|
||||||
@ -57,7 +58,8 @@ namespace Pal.Client.Rendering
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError(e, "Could not create splatoon layer {Layer} with {Count} elements", layer, elements.Count);
|
_logger.LogError(e, "Could not create splatoon layer {Layer} with {Count} elements", layer,
|
||||||
|
elements.Count);
|
||||||
_debugState.SetFromException(e);
|
_debugState.SetFromException(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -78,7 +80,7 @@ namespace Pal.Client.Rendering
|
|||||||
private string ToLayerName(ELayer layer)
|
private string ToLayerName(ELayer layer)
|
||||||
=> $"PalacePal.{layer}";
|
=> $"PalacePal.{layer}";
|
||||||
|
|
||||||
public IRenderElement CreateElement(Marker.EType type, Vector3 pos, uint color, bool fill = false)
|
public IRenderElement CreateElement(MemoryLocation.EType type, Vector3 pos, uint color, bool fill = false)
|
||||||
{
|
{
|
||||||
MarkerConfig config = MarkerConfig.ForType(type);
|
MarkerConfig config = MarkerConfig.ForType(type);
|
||||||
Element element = new Element(ElementType.CircleAtFixedCoordinates)
|
Element element = new Element(ElementType.CircleAtFixedCoordinates)
|
||||||
@ -109,8 +111,8 @@ namespace Pal.Client.Rendering
|
|||||||
|
|
||||||
var elements = new List<IRenderElement>
|
var elements = new List<IRenderElement>
|
||||||
{
|
{
|
||||||
CreateElement(Marker.EType.Trap, pos.Value, trapColor),
|
CreateElement(MemoryLocation.EType.Trap, pos.Value, trapColor),
|
||||||
CreateElement(Marker.EType.Hoard, pos.Value, hoardColor),
|
CreateElement(MemoryLocation.EType.Hoard, pos.Value, hoardColor),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!Splatoon.AddDynamicElements(ToLayerName(ELayer.Test),
|
if (!Splatoon.AddDynamicElements(ToLayerName(ELayer.Test),
|
||||||
|
@ -8,7 +8,7 @@ namespace Pal.Client.Scheduled
|
|||||||
{
|
{
|
||||||
internal interface IHandler
|
internal interface IHandler
|
||||||
{
|
{
|
||||||
void RunIfCompatible(IQueueOnFrameworkThread queued, ref bool recreateLayout, ref bool saveMarkers);
|
void RunIfCompatible(IQueueOnFrameworkThread queued, ref bool recreateLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal abstract class Handler<T> : IHandler
|
internal abstract class Handler<T> : IHandler
|
||||||
@ -21,14 +21,14 @@ namespace Pal.Client.Scheduled
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void Run(T queued, ref bool recreateLayout, ref bool saveMarkers);
|
protected abstract void Run(T queued, ref bool recreateLayout);
|
||||||
|
|
||||||
public void RunIfCompatible(IQueueOnFrameworkThread queued, ref bool recreateLayout, ref bool saveMarkers)
|
public void RunIfCompatible(IQueueOnFrameworkThread queued, ref bool recreateLayout)
|
||||||
{
|
{
|
||||||
if (queued is T t)
|
if (queued is T t)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Handling {QueuedType}", queued.GetType());
|
_logger.LogInformation("Handling {QueuedType}", queued.GetType());
|
||||||
Run(t, ref recreateLayout, ref saveMarkers);
|
Run(t, ref recreateLayout);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Pal.Client.Configuration;
|
using Pal.Client.Configuration;
|
||||||
using Pal.Client.DependencyInjection;
|
using Pal.Client.DependencyInjection;
|
||||||
|
using Pal.Client.Floors;
|
||||||
using Pal.Client.Rendering;
|
using Pal.Client.Rendering;
|
||||||
|
|
||||||
namespace Pal.Client.Scheduled
|
namespace Pal.Client.Scheduled
|
||||||
@ -9,38 +10,19 @@ namespace Pal.Client.Scheduled
|
|||||||
{
|
{
|
||||||
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedConfigUpdate>
|
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedConfigUpdate>
|
||||||
{
|
{
|
||||||
private readonly IPalacePalConfiguration _configuration;
|
|
||||||
private readonly FloorService _floorService;
|
|
||||||
private readonly TerritoryState _territoryState;
|
|
||||||
private readonly RenderAdapter _renderAdapter;
|
private readonly RenderAdapter _renderAdapter;
|
||||||
|
|
||||||
public Handler(
|
public Handler(
|
||||||
ILogger<Handler> logger,
|
ILogger<Handler> logger,
|
||||||
IPalacePalConfiguration configuration,
|
|
||||||
FloorService floorService,
|
|
||||||
TerritoryState territoryState,
|
|
||||||
RenderAdapter renderAdapter)
|
RenderAdapter renderAdapter)
|
||||||
: base(logger)
|
: base(logger)
|
||||||
{
|
{
|
||||||
_configuration = configuration;
|
|
||||||
_floorService = floorService;
|
|
||||||
_territoryState = territoryState;
|
|
||||||
_renderAdapter = renderAdapter;
|
_renderAdapter = renderAdapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Run(QueuedConfigUpdate queued, ref bool recreateLayout, ref bool saveMarkers)
|
protected override void Run(QueuedConfigUpdate queued, ref bool recreateLayout)
|
||||||
{
|
{
|
||||||
if (_configuration.Mode == EMode.Offline)
|
// TODO filter stuff if offline
|
||||||
{
|
|
||||||
LocalState.UpdateAll();
|
|
||||||
_floorService.FloorMarkers.Clear();
|
|
||||||
_floorService.EphemeralMarkers.Clear();
|
|
||||||
_territoryState.LastTerritory = 0;
|
|
||||||
|
|
||||||
recreateLayout = true;
|
|
||||||
saveMarkers = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderAdapter.ConfigUpdated();
|
_renderAdapter.ConfigUpdated();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
using Account;
|
using Account;
|
||||||
using Pal.Common;
|
using Pal.Common;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System.Numerics;
|
|
||||||
using Dalamud.Game.Gui;
|
|
||||||
using Dalamud.Logging;
|
|
||||||
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.Extensions;
|
using Pal.Client.Floors;
|
||||||
using Pal.Client.Properties;
|
using Pal.Client.Properties;
|
||||||
using Pal.Client.Windows;
|
using Pal.Client.Windows;
|
||||||
|
|
||||||
@ -31,63 +27,46 @@ namespace Pal.Client.Scheduled
|
|||||||
|
|
||||||
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedImport>
|
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedImport>
|
||||||
{
|
{
|
||||||
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||||
private readonly Chat _chat;
|
private readonly Chat _chat;
|
||||||
private readonly FloorService _floorService;
|
|
||||||
private readonly ImportService _importService;
|
private readonly ImportService _importService;
|
||||||
private readonly ConfigWindow _configWindow;
|
private readonly ConfigWindow _configWindow;
|
||||||
|
|
||||||
public Handler(
|
public Handler(
|
||||||
ILogger<Handler> logger,
|
ILogger<Handler> logger,
|
||||||
|
IServiceScopeFactory serviceScopeFactory,
|
||||||
Chat chat,
|
Chat chat,
|
||||||
FloorService floorService,
|
|
||||||
ImportService importService,
|
ImportService importService,
|
||||||
ConfigWindow configWindow)
|
ConfigWindow configWindow)
|
||||||
: base(logger)
|
: base(logger)
|
||||||
{
|
{
|
||||||
|
_serviceScopeFactory = serviceScopeFactory;
|
||||||
_chat = chat;
|
_chat = chat;
|
||||||
_floorService = floorService;
|
|
||||||
_importService = importService;
|
_importService = importService;
|
||||||
_configWindow = configWindow;
|
_configWindow = configWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Run(QueuedImport import, ref bool recreateLayout, ref bool saveMarkers)
|
protected override void Run(QueuedImport import, ref bool recreateLayout)
|
||||||
{
|
{
|
||||||
recreateLayout = true;
|
recreateLayout = true;
|
||||||
saveMarkers = true;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!Validate(import))
|
if (!Validate(import))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
List<Guid> oldExportIds = _importService.FindForServer(import.Export.ServerUrl)
|
|
||||||
.Select(x => x.Id)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
foreach (var remoteFloor in import.Export.Floors)
|
using (var scope = _serviceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
ushort territoryType = (ushort)remoteFloor.TerritoryType;
|
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
|
||||||
var localState = _floorService.GetFloorMarkers(territoryType);
|
(import.ImportedTraps, import.ImportedHoardCoffers) = _importService.Import(import.Export);
|
||||||
|
|
||||||
localState.UndoImport(oldExportIds);
|
|
||||||
ImportFloor(import, remoteFloor, localState);
|
|
||||||
|
|
||||||
localState.Save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_importService.RemoveAllByIds(oldExportIds);
|
|
||||||
_importService.RemoveById(import.ExportId);
|
|
||||||
_importService.Add(new ImportHistory
|
|
||||||
{
|
|
||||||
Id = import.ExportId,
|
|
||||||
RemoteUrl = import.Export.ServerUrl,
|
|
||||||
ExportedAt = import.Export.CreatedAt.ToDateTime(),
|
|
||||||
ImportedAt = DateTime.UtcNow,
|
|
||||||
});
|
|
||||||
_configWindow.UpdateLastImport();
|
_configWindow.UpdateLastImport();
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
$"Imported {import.ExportId} for {import.ImportedTraps} traps, {import.ImportedHoardCoffers} hoard coffers");
|
"Imported {ExportId} for {Traps} traps, {Hoard} hoard coffers", import.ExportId,
|
||||||
|
import.ImportedTraps, import.ImportedHoardCoffers);
|
||||||
_chat.Message(string.Format(Localization.ImportCompleteStatistics, import.ImportedTraps,
|
_chat.Message(string.Format(Localization.ImportCompleteStatistics, import.ImportedTraps,
|
||||||
import.ImportedHoardCoffers));
|
import.ImportedHoardCoffers));
|
||||||
}
|
}
|
||||||
@ -103,7 +82,8 @@ namespace Pal.Client.Scheduled
|
|||||||
if (import.Export.ExportVersion != ExportConfig.ExportVersion)
|
if (import.Export.ExportVersion != ExportConfig.ExportVersion)
|
||||||
{
|
{
|
||||||
_logger.LogError(
|
_logger.LogError(
|
||||||
"Import: Different version in export file, {ExportVersion} != {ConfiguredVersion}", import.Export.ExportVersion, ExportConfig.ExportVersion);
|
"Import: Different version in export file, {ExportVersion} != {ConfiguredVersion}",
|
||||||
|
import.Export.ExportVersion, ExportConfig.ExportVersion);
|
||||||
_chat.Error(Localization.Error_ImportFailed_IncompatibleVersion);
|
_chat.Error(Localization.Error_ImportFailed_IncompatibleVersion);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -127,28 +107,6 @@ namespace Pal.Client.Scheduled
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ImportFloor(QueuedImport import, ExportFloor remoteFloor, LocalState localState)
|
|
||||||
{
|
|
||||||
var remoteMarkers = remoteFloor.Objects.Select(m =>
|
|
||||||
new Marker((Marker.EType)m.Type, new Vector3(m.X, m.Y, m.Z)) { WasImported = true });
|
|
||||||
foreach (var remoteMarker in remoteMarkers)
|
|
||||||
{
|
|
||||||
Marker? localMarker = localState.Markers.SingleOrDefault(x => x == remoteMarker);
|
|
||||||
if (localMarker == null)
|
|
||||||
{
|
|
||||||
localState.Markers.Add(remoteMarker);
|
|
||||||
localMarker = remoteMarker;
|
|
||||||
|
|
||||||
if (localMarker.Type == Marker.EType.Trap)
|
|
||||||
import.ImportedTraps++;
|
|
||||||
else if (localMarker.Type == Marker.EType.Hoard)
|
|
||||||
import.ImportedHoardCoffers++;
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteMarker.Imports.Add(import.ExportId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Pal.Client.Configuration;
|
using Pal.Client.Configuration;
|
||||||
using Pal.Client.DependencyInjection;
|
using Pal.Client.DependencyInjection;
|
||||||
using Pal.Client.Extensions;
|
using Pal.Client.Extensions;
|
||||||
|
using Pal.Client.Floors;
|
||||||
|
using Pal.Client.Floors.Tasks;
|
||||||
using Pal.Client.Net;
|
using Pal.Client.Net;
|
||||||
|
|
||||||
namespace Pal.Client.Scheduled
|
namespace Pal.Client.Scheduled
|
||||||
@ -14,10 +17,11 @@ namespace Pal.Client.Scheduled
|
|||||||
public required SyncType Type { get; init; }
|
public required SyncType Type { get; init; }
|
||||||
public required ushort TerritoryType { get; init; }
|
public required ushort TerritoryType { get; init; }
|
||||||
public required bool Success { get; init; }
|
public required bool Success { get; init; }
|
||||||
public required List<Marker> Markers { get; init; }
|
public required IReadOnlyList<PersistentLocation> Locations { get; init; }
|
||||||
|
|
||||||
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedSyncResponse>
|
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedSyncResponse>
|
||||||
{
|
{
|
||||||
|
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||||
private readonly IPalacePalConfiguration _configuration;
|
private readonly IPalacePalConfiguration _configuration;
|
||||||
private readonly FloorService _floorService;
|
private readonly FloorService _floorService;
|
||||||
private readonly TerritoryState _territoryState;
|
private readonly TerritoryState _territoryState;
|
||||||
@ -25,46 +29,56 @@ namespace Pal.Client.Scheduled
|
|||||||
|
|
||||||
public Handler(
|
public Handler(
|
||||||
ILogger<Handler> logger,
|
ILogger<Handler> logger,
|
||||||
|
IServiceScopeFactory serviceScopeFactory,
|
||||||
IPalacePalConfiguration configuration,
|
IPalacePalConfiguration configuration,
|
||||||
FloorService floorService,
|
FloorService floorService,
|
||||||
TerritoryState territoryState,
|
TerritoryState territoryState,
|
||||||
DebugState debugState)
|
DebugState debugState)
|
||||||
: base(logger)
|
: base(logger)
|
||||||
{
|
{
|
||||||
|
_serviceScopeFactory = serviceScopeFactory;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_floorService = floorService;
|
_floorService = floorService;
|
||||||
_territoryState = territoryState;
|
_territoryState = territoryState;
|
||||||
_debugState = debugState;
|
_debugState = debugState;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Run(QueuedSyncResponse queued, ref bool recreateLayout, ref bool saveMarkers)
|
protected override void Run(QueuedSyncResponse queued, ref bool recreateLayout)
|
||||||
{
|
{
|
||||||
recreateLayout = true;
|
recreateLayout = true;
|
||||||
saveMarkers = true;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var remoteMarkers = queued.Markers;
|
var remoteMarkers = queued.Locations;
|
||||||
var currentFloor = _floorService.GetFloorMarkers(queued.TerritoryType);
|
var memoryTerritory = _floorService.GetTerritoryIfReady(queued.TerritoryType);
|
||||||
if (_configuration.Mode == EMode.Online && queued.Success && remoteMarkers.Count > 0)
|
if (memoryTerritory != null && _configuration.Mode == EMode.Online && queued.Success &&
|
||||||
|
remoteMarkers.Count > 0)
|
||||||
{
|
{
|
||||||
switch (queued.Type)
|
switch (queued.Type)
|
||||||
{
|
{
|
||||||
case SyncType.Download:
|
case SyncType.Download:
|
||||||
case SyncType.Upload:
|
case SyncType.Upload:
|
||||||
|
List<PersistentLocation> newLocations = new();
|
||||||
foreach (var remoteMarker in remoteMarkers)
|
foreach (var remoteMarker in remoteMarkers)
|
||||||
{
|
{
|
||||||
// Both uploads and downloads return the network id to be set, but only the downloaded marker is new as in to-be-saved.
|
// Both uploads and downloads return the network id to be set, but only the downloaded marker is new as in to-be-saved.
|
||||||
Marker? localMarker = currentFloor.Markers.SingleOrDefault(x => x == remoteMarker);
|
PersistentLocation? localLocation =
|
||||||
if (localMarker != null)
|
memoryTerritory.Locations.SingleOrDefault(x => x == remoteMarker);
|
||||||
|
if (localLocation != null)
|
||||||
{
|
{
|
||||||
localMarker.NetworkId = remoteMarker.NetworkId;
|
localLocation.NetworkId = remoteMarker.NetworkId;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queued.Type == SyncType.Download)
|
if (queued.Type == SyncType.Download)
|
||||||
currentFloor.Markers.Add(remoteMarker);
|
{
|
||||||
|
memoryTerritory.Locations.Add(remoteMarker);
|
||||||
|
newLocations.Add(remoteMarker);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newLocations.Count > 0)
|
||||||
|
new SaveNewLocations(_serviceScopeFactory, memoryTerritory, newLocations).Start();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -73,11 +87,23 @@ namespace Pal.Client.Scheduled
|
|||||||
_configuration.FindAccount(RemoteApi.RemoteUrl)?.AccountId.ToPartialId();
|
_configuration.FindAccount(RemoteApi.RemoteUrl)?.AccountId.ToPartialId();
|
||||||
if (partialAccountId == null)
|
if (partialAccountId == null)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
List<PersistentLocation> locationsToUpdate = new();
|
||||||
foreach (var remoteMarker in remoteMarkers)
|
foreach (var remoteMarker in remoteMarkers)
|
||||||
{
|
{
|
||||||
Marker? localMarker = currentFloor.Markers.SingleOrDefault(x => x == remoteMarker);
|
PersistentLocation? localLocation =
|
||||||
if (localMarker != null)
|
memoryTerritory.Locations.SingleOrDefault(x => x == remoteMarker);
|
||||||
localMarker.RemoteSeenOn.Add(partialAccountId);
|
if (localLocation != null)
|
||||||
|
{
|
||||||
|
localLocation.RemoteSeenOn.Add(partialAccountId);
|
||||||
|
locationsToUpdate.Add(localLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locationsToUpdate.Count > 0)
|
||||||
|
{
|
||||||
|
new MarkRemoteSeen(_serviceScopeFactory, memoryTerritory, locationsToUpdate,
|
||||||
|
partialAccountId).Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -91,22 +117,22 @@ namespace Pal.Client.Scheduled
|
|||||||
if (queued.Type == SyncType.Download)
|
if (queued.Type == SyncType.Download)
|
||||||
{
|
{
|
||||||
if (queued.Success)
|
if (queued.Success)
|
||||||
_territoryState.TerritorySyncState = SyncState.Complete;
|
_territoryState.TerritorySyncState = ESyncState.Complete;
|
||||||
else
|
else
|
||||||
_territoryState.TerritorySyncState = SyncState.Failed;
|
_territoryState.TerritorySyncState = ESyncState.Failed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_debugState.SetFromException(e);
|
_debugState.SetFromException(e);
|
||||||
if (queued.Type == SyncType.Download)
|
if (queued.Type == SyncType.Download)
|
||||||
_territoryState.TerritorySyncState = SyncState.Failed;
|
_territoryState.TerritorySyncState = ESyncState.Failed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SyncState
|
public enum ESyncState
|
||||||
{
|
{
|
||||||
NotAttempted,
|
NotAttempted,
|
||||||
NotNeeded,
|
NotNeeded,
|
||||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Pal.Client.Configuration;
|
using Pal.Client.Configuration;
|
||||||
using Pal.Client.DependencyInjection;
|
using Pal.Client.DependencyInjection;
|
||||||
|
using Pal.Client.Floors;
|
||||||
using Pal.Client.Windows;
|
using Pal.Client.Windows;
|
||||||
using Pal.Common;
|
using Pal.Common;
|
||||||
|
|
||||||
@ -20,28 +21,18 @@ namespace Pal.Client.Scheduled
|
|||||||
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedUndoImport>
|
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedUndoImport>
|
||||||
{
|
{
|
||||||
private readonly ImportService _importService;
|
private readonly ImportService _importService;
|
||||||
private readonly FloorService _floorService;
|
|
||||||
private readonly ConfigWindow _configWindow;
|
private readonly ConfigWindow _configWindow;
|
||||||
|
|
||||||
public Handler(ILogger<Handler> logger, ImportService importService, FloorService floorService, ConfigWindow configWindow)
|
public Handler(ILogger<Handler> logger, ImportService importService, ConfigWindow configWindow)
|
||||||
: base(logger)
|
: base(logger)
|
||||||
{
|
{
|
||||||
_importService = importService;
|
_importService = importService;
|
||||||
_floorService = floorService;
|
|
||||||
_configWindow = configWindow;
|
_configWindow = configWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Run(QueuedUndoImport queued, ref bool recreateLayout, ref bool saveMarkers)
|
protected override void Run(QueuedUndoImport queued, ref bool recreateLayout)
|
||||||
{
|
{
|
||||||
recreateLayout = true;
|
recreateLayout = true;
|
||||||
saveMarkers = true;
|
|
||||||
|
|
||||||
foreach (ETerritoryType territoryType in typeof(ETerritoryType).GetEnumValues())
|
|
||||||
{
|
|
||||||
var localState = _floorService.GetFloorMarkers((ushort)territoryType);
|
|
||||||
localState.UndoImport(new List<Guid> { queued.ExportId });
|
|
||||||
localState.Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
_importService.RemoveById(queued.ExportId);
|
_importService.RemoveById(queued.ExportId);
|
||||||
_configWindow.UpdateLastImport();
|
_configWindow.UpdateLastImport();
|
||||||
|
@ -25,6 +25,7 @@ using Pal.Client.Configuration;
|
|||||||
using Pal.Client.Database;
|
using Pal.Client.Database;
|
||||||
using Pal.Client.DependencyInjection;
|
using Pal.Client.DependencyInjection;
|
||||||
using Pal.Client.Extensions;
|
using Pal.Client.Extensions;
|
||||||
|
using Pal.Client.Floors;
|
||||||
|
|
||||||
namespace Pal.Client.Windows
|
namespace Pal.Client.Windows
|
||||||
{
|
{
|
||||||
@ -382,24 +383,25 @@ namespace Pal.Client.Windows
|
|||||||
ImGui.Text($"{_debugState.DebugMessage}");
|
ImGui.Text($"{_debugState.DebugMessage}");
|
||||||
|
|
||||||
ImGui.Indent();
|
ImGui.Indent();
|
||||||
if (_floorService.FloorMarkers.TryGetValue(_territoryState.LastTerritory, out var currentFloor))
|
MemoryTerritory? memoryTerritory = _floorService.GetTerritoryIfReady(_territoryState.LastTerritory);
|
||||||
|
if (memoryTerritory != null)
|
||||||
{
|
{
|
||||||
if (_trapConfig.Show)
|
if (_trapConfig.Show)
|
||||||
{
|
{
|
||||||
int traps = currentFloor.Markers.Count(x => x.Type == Marker.EType.Trap);
|
int traps = memoryTerritory.Locations.Count(x => x.Type == MemoryLocation.EType.Trap);
|
||||||
ImGui.Text($"{traps} known trap{(traps == 1 ? "" : "s")}");
|
ImGui.Text($"{traps} known trap{(traps == 1 ? "" : "s")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_hoardConfig.Show)
|
if (_hoardConfig.Show)
|
||||||
{
|
{
|
||||||
int hoardCoffers = currentFloor.Markers.Count(x => x.Type == Marker.EType.Hoard);
|
int hoardCoffers = memoryTerritory.Locations.Count(x => x.Type == MemoryLocation.EType.Hoard);
|
||||||
ImGui.Text($"{hoardCoffers} known hoard coffer{(hoardCoffers == 1 ? "" : "s")}");
|
ImGui.Text($"{hoardCoffers} known hoard coffer{(hoardCoffers == 1 ? "" : "s")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_silverConfig.Show)
|
if (_silverConfig.Show)
|
||||||
{
|
{
|
||||||
int silverCoffers =
|
int silverCoffers =
|
||||||
_floorService.EphemeralMarkers.Count(x => x.Type == Marker.EType.SilverCoffer);
|
_floorService.EphemeralLocations.Count(x => x.Type == MemoryLocation.EType.SilverCoffer);
|
||||||
ImGui.Text(
|
ImGui.Text(
|
||||||
$"{silverCoffers} silver coffer{(silverCoffers == 1 ? "" : "s")} visible on current floor");
|
$"{silverCoffers} silver coffer{(silverCoffers == 1 ? "" : "s")} visible on current floor");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user