Compare commits

...

60 Commits
v3.3 ... master

Author SHA1 Message Date
Liza d239a6e4b0
API 10 2024-07-06 13:26:39 +02:00
Liza 9d5e4a797b
Update Dockerfile 2024-07-06 13:20:17 +02:00
Liza 3e2f917c14
API 10 2024-07-06 13:14:59 +02:00
Liza 2fdf9b1e4d
Remove GitInfo dependency 2024-01-16 11:41:13 +01:00
Liza 18ffd66086
Update icon URL 2024-01-16 07:03:56 +01:00
Liza 64e576a004
Render: Ensure RenderElement is null if we don't recreate it 2023-11-30 14:01:59 +01:00
Liza 0d1882d97f
Render: Performance fix 2023-11-26 22:14:04 +01:00
Liza 964119cfd2
Render: Use enabled instead of color for showing/hiding elements 2023-11-26 20:13:17 +01:00
Liza 309edfcd17
Bump version/recompile 2023-11-14 20:21:40 +01:00
Liza 8fbd3fbc0d
Update docker build 2023-11-11 01:56:09 +01:00
Liza ce9be45e8f
Update docker build 2023-11-11 01:54:43 +01:00
Liza 8def5e28a4
Update dependencies 2023-11-11 01:41:15 +01:00
Liza ba06576c2d
Update dependencies 2023-11-11 01:39:17 +01:00
Liza f259f03534
Update server submodule structure 2023-11-11 01:39:10 +01:00
Liza cf3a24afe8
Update header icon logic for Dalamud changes 2023-11-09 11:32:32 +01:00
Liza 2a180345eb
Update submodule URL 2023-10-14 00:02:21 +02:00
Liza 345c9c59a3
Add LLib, remove outdated info, add config window constraints 2023-10-11 03:56:18 +02:00
Liza 5c4e9f30e0
API 9 2023-10-03 22:05:19 +02:00
Liza d337413b82 Update docker-build.sh 2023-10-03 17:54:53 +00:00
Liza 5a5d7fecfd
Change nuget lib path 2023-10-03 11:10:43 +02:00
Liza 1ee86d378f
API 9 2023-10-03 11:08:38 +02:00
Liza cd52192d15
Setup server/test submodules 2023-09-17 22:28:37 +02:00
Liza 6260f35b61 Update repo location 2023-07-31 02:58:24 +02:00
Liza 79ad7fbc39 Update README 2023-07-30 23:53:47 +02:00
Liza 1bdcc7179c Update image location 2023-07-30 23:26:15 +02:00
Liza 5ba18477b4 Update repo url 2023-07-30 23:22:33 +02:00
Liza 5f506bda8a Update server url 2023-07-30 22:32:39 +02:00
Liza bbec57c3ad Update actions 2023-07-30 21:49:14 +02:00
Liza 58404b9728 Fix floor change regex (for pomanders)
Not sure how or why that even broke, the string doesn't seem to have changed in the LogMessage sheet.

Fixes #18.
2023-04-13 09:17:06 +02:00
Liza 8042aee951 Use file-scoped namespaces 2023-03-30 22:02:51 +02:00
Liza 0b07e8b8f6 Code cleanup 2023-03-27 20:00:30 +02:00
Liza 7e73430179 Add palace service tests, remove standalone client 2023-03-27 18:02:18 +02:00
Liza cec3597629 Update dependencies 2023-03-26 15:57:50 +02:00
Liza ae29eaa52f Update ECommons 2023-03-26 15:49:52 +02:00
Liza b81ced33e8 Code style tweaks 2023-03-26 15:47:43 +02:00
Liza 87806397d1 Configure GHA caching 2023-03-26 14:26:12 +02:00
Liza d645aa9ea7 Configure GHA dotnet test timeout 2023-03-26 13:55:30 +02:00
Liza 4288440e6c Add export tests 2023-03-26 03:11:01 +02:00
Liza 36b328c29e Add simple account test 2023-03-25 19:14:32 +01:00
Liza 232ab164be Update DE localization 2023-03-25 02:47:21 +01:00
Liza a4890c0159 Fix duplicate seen location errors 2023-03-25 02:39:54 +01:00
Liza 7bdf97411c Hide occasional FileLoadException (temporary error) 2023-03-11 01:50:26 +01:00
Liza 3e9f14419c Update gRPC packages 2023-03-10 03:08:44 +01:00
Liza a21e9335aa GHA: Add workflow_dispatch to docker build 2023-03-10 02:44:04 +01:00
Liza 1b415d7a7f Improve repo exception handling 2023-03-08 01:27:10 +01:00
Liza c5acb2ca54 Eureka Orthos: Add owlet trap to pomander of sight 2023-03-07 23:28:58 +01:00
Liza 7a514fad2a Add debug feature for object table 2023-03-07 21:55:50 +01:00
Liza 125b687a9c Eureka Orthos: Territory types 2023-03-07 12:58:10 +01:00
Liza 07ed62cd9a Update JP & DE localization 2023-03-02 02:00:05 +01:00
Liza 7179d41e59 Fix local seen 2023-02-26 17:54:15 +01:00
Liza 8279bfe9bf Fix config changes to trap/coffer locations not being applied when saving 2023-02-26 17:43:20 +01:00
Liza e45b72a655 Add gold coffers (#10) 2023-02-26 17:31:37 +01:00
Liza cbdcf58063 Move logging into nuget package 2023-02-25 15:32:26 +01:00
Liza d25e21619d Merge branch 'signtool-fix' 2023-02-25 01:35:26 +01:00
Liza 2bfbeacdd0 fix CI build 2023-02-25 01:32:39 +01:00
Liza 88ec7a00da Improve error handling if a command fails with an exception 2023-02-25 01:15:19 +01:00
Liza 0c922c1695 Use PalConfigCommand for the config UI button (partial revert of previous commit) 2023-02-25 01:07:10 +01:00
Liza e1dc8cafd9 Fix /pal test-connection not working if using Simple renderer 2023-02-25 01:06:33 +01:00
Liza e79e8de6dc Use ISubCommand for subcommands 2023-02-25 00:55:48 +01:00
Liza f140fb870c Signing test 2023-02-24 16:21:38 +01:00
104 changed files with 5605 additions and 5119 deletions

View File

@ -4,4 +4,6 @@
.gitignore .gitignore
Dockerfile Dockerfile
.dockerignore .dockerignore
docker-build.sh
.vs/ .vs/
.idea/

31
.editorconfig Normal file
View File

@ -0,0 +1,31 @@
root = true
[*]
indent_style = space
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[{*.cs}]
charset = utf-8-bom
indent_size = 4
dotnet_sort_system_directives_first = true
csharp_style_implicit_object_creation_when_type_is_apparent = true
csharp_trailing_comma_in_multiline_lists = true
csharp_place_simple_embedded_block_on_same_line = false
csharp_place_attribute_on_same_line = false
resharper_indent_text = ZeroIndent
# methods
csharp_style_expression_bodied_methods = true
# namespaces
csharp_style_namespace_declarations = file_scoped
# braces
csharp_prefer_braces = when_multiline
[{*.resx,*.Designer.cs,packages.lock.json}]
generated_code = true
ij_formatter_enabled = false

View File

@ -1,29 +0,0 @@
name: dotnet build
on:
push:
paths-ignore:
- '**.md'
- 'Dockerfile'
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0
- name: Download Dalamud
run: |
Invoke-WebRequest -Uri https://goatcorp.github.io/dalamud-distrib/latest.zip -OutFile latest.zip
Expand-Archive -Force latest.zip "$env:AppData\XIVLauncher\addon\Hooks\dev\"
- name: Install dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore

View File

@ -1,30 +0,0 @@
name: docker build
on:
push:
branches:
- master
paths:
- '.github/workflows/server.yml'
- 'Pal.Common/**'
- 'Pal.Server/**'
- 'Dockerfile'
permissions:
packages: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Login to GitHub Package Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
with:
push: true
tags: ghcr.io/${{ github.repository_owner }}/palace-pal:latest

View File

@ -1,24 +0,0 @@
name: Upload to Crowdin
on:
push:
branches:
- master
paths:
- 'Pal.Client/Properties/*.resx'
- 'crowdin.yml'
workflow_dispatch: {}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Crowdin Action
uses: crowdin/github-action@1.4.9
with:
upload_sources: true
upload_translations: true
download_translations: false
crowdin_branch_name: ${{ github.ref_name }}
env:
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

6
.gitmodules vendored
View File

@ -1,3 +1,9 @@
[submodule "vendor/ECommons"] [submodule "vendor/ECommons"]
path = vendor/ECommons path = vendor/ECommons
url = https://github.com/NightmareXIV/ECommons url = https://github.com/NightmareXIV/ECommons
[submodule "vendor/LLib"]
path = vendor/LLib
url = https://git.carvel.li/liza/LLib.git
[submodule "Server"]
path = Server
url = https://git.carvel.li/liza/PalacePal.Server.git

10
Directory.Build.targets Normal file
View File

@ -0,0 +1,10 @@
<Project>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<WindowsKitsRoot Condition="'$(WindowsKitsRoot)' == ''">$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots', 'KitsRoot10', null, RegistryView.Registry32, RegistryView.Default))</WindowsKitsRoot>
<SignToolPath Condition="'$(WindowsKitsRoot)' != '' And '$(SignToolPath)' == '' And exists('$(WindowsKitsRoot)bin\10.0.19041.0\')">$(WindowsKitsRoot)bin\10.0.19041.0\x86\</SignToolPath>
</PropertyGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(SignToolPath)' != '' And Exists('$(SolutionDir)codesigning.pfx')">
<Exec Command="&quot;$(SignToolPath)signtool.exe&quot; sign /f $(SolutionDir)codesigning.pfx /t http://timestamp.digicert.com /fd SHA256 &quot;$(TargetPath)&quot;"/>
</Target>
</Project>

View File

@ -1,13 +1,19 @@
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
ARG TARGETARCH
WORKDIR /build WORKDIR /build
COPY Pal.Common/Pal.Common.csproj Pal.Common/ COPY Pal.Common/Pal.Common.csproj Pal.Common/
COPY Pal.Server/Pal.Server.csproj Pal.Server/ COPY Server/Server/Pal.Server.csproj Server/Server/
RUN dotnet restore Pal.Server/Pal.Server.csproj RUN dotnet restore Server/Server/Pal.Server.csproj -a $TARGETARCH
COPY . ./ COPY . ./
RUN dotnet publish Pal.Server/Pal.Server.csproj --configuration Release --no-restore -o /dist RUN dotnet publish Server/Server/Pal.Server.csproj -a $TARGETARCH --no-restore -o /dist
FROM mcr.microsoft.com/dotnet/aspnet:8.0
# fix later
ENV DOTNET_ROLL_FORWARD=Major
ENV DOTNET_ROLL_FORWARD_PRE_RELEASE=1
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS runtime
EXPOSE 5415 EXPOSE 5415
ENV DOTNET_ENVIRONMENT=Production ENV DOTNET_ENVIRONMENT=Production
ENV ASPNETCORE_URLS= ENV ASPNETCORE_URLS=

View File

@ -0,0 +1,9 @@
using System;
using System.Collections.Generic;
namespace Pal.Client.Commands;
public interface ISubCommand
{
IReadOnlyDictionary<string, Action<string>> GetHandlers();
}

View File

@ -1,11 +1,12 @@
using Dalamud.Interface.Windowing; using System;
using System.Collections.Generic;
using Pal.Client.Configuration; using Pal.Client.Configuration;
using Pal.Client.Windows; using Pal.Client.Windows;
namespace Pal.Client.Commands namespace Pal.Client.Commands;
internal class PalConfigCommand : ISubCommand
{ {
internal class PalConfigCommand
{
private readonly IPalacePalConfiguration _configuration; private readonly IPalacePalConfiguration _configuration;
private readonly AgreementWindow _agreementWindow; private readonly AgreementWindow _agreementWindow;
private readonly ConfigWindow _configWindow; private readonly ConfigWindow _configWindow;
@ -20,6 +21,14 @@ namespace Pal.Client.Commands
_configWindow = configWindow; _configWindow = configWindow;
} }
public IReadOnlyDictionary<string, Action<string>> GetHandlers()
=> new Dictionary<string, Action<string>>
{
{ "config", _ => Execute() },
{ "", _ => Execute() }
};
public void Execute() public void Execute()
{ {
if (_configuration.FirstUse) if (_configuration.FirstUse)
@ -27,5 +36,4 @@ namespace Pal.Client.Commands
else else
_configWindow.Toggle(); _configWindow.Toggle();
} }
}
} }

View File

@ -1,21 +1,22 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Dalamud.Game.ClientState; using Dalamud.Plugin.Services;
using Pal.Client.DependencyInjection; using Pal.Client.DependencyInjection;
using Pal.Client.Extensions; using Pal.Client.Extensions;
using Pal.Client.Floors; using Pal.Client.Floors;
using Pal.Client.Rendering; using Pal.Client.Rendering;
namespace Pal.Client.Commands namespace Pal.Client.Commands;
internal sealed class PalNearCommand : ISubCommand
{ {
internal sealed class PalNearCommand
{
private readonly Chat _chat; private readonly Chat _chat;
private readonly ClientState _clientState; private readonly IClientState _clientState;
private readonly TerritoryState _territoryState; private readonly TerritoryState _territoryState;
private readonly FloorService _floorService; private readonly FloorService _floorService;
public PalNearCommand(Chat chat, ClientState clientState, TerritoryState territoryState, public PalNearCommand(Chat chat, IClientState clientState, TerritoryState territoryState,
FloorService floorService) FloorService floorService)
{ {
_chat = chat; _chat = chat;
@ -24,23 +25,14 @@ namespace Pal.Client.Commands
_floorService = floorService; _floorService = floorService;
} }
public void Execute(string arguments)
{
switch (arguments)
{
default:
DebugNearest(_ => true);
break;
case "tnear": public IReadOnlyDictionary<string, Action<string>> GetHandlers()
DebugNearest(m => m.Type == MemoryLocation.EType.Trap); => new Dictionary<string, Action<string>>
break; {
{ "near", _ => DebugNearest(_ => true) },
case "hnear": { "tnear", _ => DebugNearest(m => m.Type == MemoryLocation.EType.Trap) },
DebugNearest(m => m.Type == MemoryLocation.EType.Hoard); { "hnear", _ => DebugNearest(m => m.Type == MemoryLocation.EType.Hoard) },
break; };
}
}
private void DebugNearest(Predicate<PersistentLocation> predicate) private void DebugNearest(Predicate<PersistentLocation> predicate)
{ {
@ -54,11 +46,11 @@ namespace Pal.Client.Commands
var playerPosition = _clientState.LocalPlayer?.Position; var playerPosition = _clientState.LocalPlayer?.Position;
if (playerPosition == null) if (playerPosition == null)
return; return;
_chat.Message($"{playerPosition}"); _chat.Message($"Your position: {playerPosition}");
var nearbyMarkers = state.Locations 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.Enabled)
.Select(m => new { m, distance = (playerPosition.Value - m.Position).Length() }) .Select(m => new { m, distance = (playerPosition.Value - m.Position).Length() })
.OrderBy(m => m.distance) .OrderBy(m => m.distance)
.Take(5) .Take(5)
@ -67,5 +59,4 @@ namespace Pal.Client.Commands
_chat.UnformattedMessage( _chat.UnformattedMessage(
$"{nearbyMarker.distance:F2} - {nearbyMarker.m.Type} {nearbyMarker.m.NetworkId?.ToPartialId(length: 8)} - {nearbyMarker.m.Position}"); $"{nearbyMarker.distance:F2} - {nearbyMarker.m.Type} {nearbyMarker.m.NetworkId?.ToPartialId(length: 8)} - {nearbyMarker.m.Position}");
} }
}
} }

View File

@ -1,10 +1,11 @@
using System; using System;
using System.Collections.Generic;
using Pal.Client.DependencyInjection; using Pal.Client.DependencyInjection;
namespace Pal.Client.Commands namespace Pal.Client.Commands;
internal sealed class PalStatsCommand : ISubCommand
{ {
internal sealed class PalStatsCommand
{
private readonly StatisticsService _statisticsService; private readonly StatisticsService _statisticsService;
public PalStatsCommand(StatisticsService statisticsService) public PalStatsCommand(StatisticsService statisticsService)
@ -12,7 +13,12 @@ namespace Pal.Client.Commands
_statisticsService = statisticsService; _statisticsService = statisticsService;
} }
public void Execute() public IReadOnlyDictionary<string, Action<string>> GetHandlers()
=> new Dictionary<string, Action<string>>
{
{ "stats", _ => Execute() },
};
private void Execute()
=> _statisticsService.ShowGlobalStatistics(); => _statisticsService.ShowGlobalStatistics();
}
} }

View File

@ -1,10 +1,12 @@
using ECommons.Schedulers; using System;
using System.Collections.Generic;
using ECommons.Schedulers;
using Pal.Client.Windows; using Pal.Client.Windows;
namespace Pal.Client.Commands namespace Pal.Client.Commands;
internal sealed class PalTestConnectionCommand : ISubCommand
{ {
internal sealed class PalTestConnectionCommand
{
private readonly ConfigWindow _configWindow; private readonly ConfigWindow _configWindow;
public PalTestConnectionCommand(ConfigWindow configWindow) public PalTestConnectionCommand(ConfigWindow configWindow)
@ -12,9 +14,16 @@ namespace Pal.Client.Commands
_configWindow = configWindow; _configWindow = configWindow;
} }
public void Execute() public IReadOnlyDictionary<string, Action<string>> GetHandlers()
=> new Dictionary<string, Action<string>>
{ {
{ "test-connection", _ => Execute() },
{ "tc", _ => Execute() },
};
private void Execute()
{
_configWindow.IsOpen = true;
var _ = new TickScheduler(() => _configWindow.TestConnection()); var _ = new TickScheduler(() => _configWindow.TestConnection());
} }
}
} }

View File

@ -2,19 +2,14 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Dalamud.Logging;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Pal.Client.DependencyInjection.Logging;
namespace Pal.Client.Configuration namespace Pal.Client.Configuration;
public sealed class AccountConfigurationV7 : IAccountConfiguration
{ {
public sealed class AccountConfigurationV7 : IAccountConfiguration
{
private const int DefaultEntropyLength = 16; private const int DefaultEntropyLength = 16;
private static readonly ILogger _logger =
DependencyInjectionContext.LoggerProvider.CreateLogger<AccountConfigurationV7>();
[JsonConstructor] [JsonConstructor]
public AccountConfigurationV7() public AccountConfigurationV7()
{ {
@ -77,9 +72,8 @@ namespace Pal.Client.Configuration
byte[] guidBytes = ProtectedData.Unprotect(Convert.FromBase64String(EncryptedId), Entropy, DataProtectionScope.CurrentUser); byte[] guidBytes = ProtectedData.Unprotect(Convert.FromBase64String(EncryptedId), Entropy, DataProtectionScope.CurrentUser);
return new Guid(guidBytes); return new Guid(guidBytes);
} }
catch (Exception e) catch (Exception)
{ {
_logger.LogTrace(e, "Could not load account id {Id}", EncryptedId);
return null; return null;
} }
} }
@ -147,5 +141,4 @@ namespace Pal.Client.Configuration
/// </summary> /// </summary>
ProtectedDataUnsupported = 3, ProtectedDataUnsupported = 3,
} }
}
} }

View File

@ -3,19 +3,17 @@ using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Pal.Client.Configuration namespace Pal.Client.Configuration;
{
internal static class ConfigurationData
{
private static readonly ILogger _logger =
DependencyInjectionContext.LoggerProvider.CreateLogger(typeof(ConfigurationData));
internal static class ConfigurationData
{
[Obsolete("for V1 import")] [Obsolete("for V1 import")]
internal static readonly byte[] FixedV1Entropy = { 0x22, 0x4b, 0xe7, 0x21, 0x44, 0x83, 0x69, 0x55, 0x80, 0x38 }; internal static readonly byte[] FixedV1Entropy = { 0x22, 0x4b, 0xe7, 0x21, 0x44, 0x83, 0x69, 0x55, 0x80, 0x38 };
public const string ConfigFileName = "palace-pal.config.json"; public const string ConfigFileName = "palace-pal.config.json";
private static bool? _supportsDpapi = null; private static bool? _supportsDpapi;
public static bool SupportsDpapi public static bool SupportsDpapi
{ {
get get
@ -34,11 +32,9 @@ namespace Pal.Client.Configuration
{ {
_supportsDpapi = false; _supportsDpapi = false;
} }
_logger.LogTrace("DPAPI support: {Supported}", _supportsDpapi);
} }
return _supportsDpapi.Value; return _supportsDpapi.Value;
} }
} }
}
} }

View File

@ -13,17 +13,17 @@ using Pal.Client.Configuration.Legacy;
using Pal.Client.Database; using Pal.Client.Database;
using NJson = Newtonsoft.Json; using NJson = Newtonsoft.Json;
namespace Pal.Client.Configuration namespace Pal.Client.Configuration;
internal sealed class ConfigurationManager
{ {
internal sealed class ConfigurationManager
{
private readonly ILogger<ConfigurationManager> _logger; private readonly ILogger<ConfigurationManager> _logger;
private readonly DalamudPluginInterface _pluginInterface; private readonly IDalamudPluginInterface _pluginInterface;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
public event EventHandler<IPalacePalConfiguration>? Saved; public event EventHandler<IPalacePalConfiguration>? Saved;
public ConfigurationManager(ILogger<ConfigurationManager> logger, DalamudPluginInterface pluginInterface, public ConfigurationManager(ILogger<ConfigurationManager> logger, IDalamudPluginInterface pluginInterface,
IServiceProvider serviceProvider) IServiceProvider serviceProvider)
{ {
_logger = logger; _logger = logger;
@ -87,7 +87,9 @@ namespace Pal.Client.Configuration
dbContext.Imports.Add(new ImportHistory dbContext.Imports.Add(new ImportHistory
{ {
Id = importHistory.Id, Id = importHistory.Id,
RemoteUrl = importHistory.RemoteUrl?.Replace(".μ.tv", ".liza.sh"), RemoteUrl = importHistory.RemoteUrl
?.Replace(".μ.tv", ".liza.sh")
.Replace("pal.liza.sh", "connect.palacepal.com"),
ExportedAt = importHistory.ExportedAt, ExportedAt = importHistory.ExportedAt,
ImportedAt = importHistory.ImportedAt ImportedAt = importHistory.ImportedAt
}); });
@ -98,6 +100,17 @@ namespace Pal.Client.Configuration
File.Move(_pluginInterface.ConfigFile.FullName, _pluginInterface.ConfigFile.FullName + ".old", true); File.Move(_pluginInterface.ConfigFile.FullName, _pluginInterface.ConfigFile.FullName + ".old", true);
} }
IPalacePalConfiguration? currentConfig = Load();
IAccountConfiguration? legacyAccount = currentConfig?.FindAccount("https://pal.liza.sh");
if (currentConfig != null && legacyAccount != null)
{
IAccountConfiguration newAccount = currentConfig.CreateAccount("https://connect.palacepal.com", legacyAccount.AccountId);
newAccount.CachedRoles = legacyAccount.CachedRoles;
currentConfig.RemoveAccount(legacyAccount.Server);
Save(currentConfig, false);
}
} }
private ConfigurationV7 MigrateToV7(ConfigurationV1 v1) private ConfigurationV7 MigrateToV7(ConfigurationV1 v1)
@ -141,7 +154,9 @@ namespace Pal.Client.Configuration
if (string.IsNullOrEmpty(accountId)) if (string.IsNullOrEmpty(accountId))
continue; continue;
string serverName = server.Replace(".μ.tv", ".liza.sh"); string serverName = server
.Replace(".μ.tv", ".liza.sh")
.Replace("pal.liza.sh", "connect.palacepal.com");
IAccountConfiguration newAccount = v7.CreateAccount(serverName, accountId); IAccountConfiguration newAccount = v7.CreateAccount(serverName, accountId);
newAccount.CachedRoles = oldAccount.CachedRoles.ToList(); newAccount.CachedRoles = oldAccount.CachedRoles.ToList();
} }
@ -152,5 +167,4 @@ namespace Pal.Client.Configuration
} }
#pragma warning restore CS0618 #pragma warning restore CS0618
#pragma warning restore CS0612 #pragma warning restore CS0612
}
} }

View File

@ -2,10 +2,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Pal.Client.Configuration namespace Pal.Client.Configuration;
public sealed class ConfigurationV7 : IPalacePalConfiguration, IConfigurationInConfigDirectory
{ {
public sealed class ConfigurationV7 : IPalacePalConfiguration, IConfigurationInConfigDirectory
{
public int Version { get; set; } = 7; public int Version { get; set; } = 7;
public bool FirstUse { get; set; } = true; public bool FirstUse { get; set; } = true;
@ -50,5 +50,4 @@ namespace Pal.Client.Configuration
var account = FindAccount(server); var account = FindAccount(server);
return account == null || account.CachedRoles.Contains(role); return account == null || account.CachedRoles.Contains(role);
} }
}
} }

View File

@ -1,15 +1,14 @@
namespace Pal.Client.Configuration namespace Pal.Client.Configuration;
public enum EMode
{ {
public enum EMode
{
/// <summary> /// <summary>
/// Fetches trap locations from remote server. /// Fetches trap locations from remote server.
/// </summary> /// </summary>
Online = 1, Online = 1,
/// <summary> /// <summary>
/// Only shows traps found by yourself uisng a pomander of sight. /// Only shows traps found by yourself using a pomander of sight.
/// </summary> /// </summary>
Offline = 2, Offline = 2,
}
} }

View File

@ -1,11 +1,10 @@
namespace Pal.Client.Configuration namespace Pal.Client.Configuration;
public enum ERenderer
{ {
public enum ERenderer
{
/// <see cref="Rendering.SimpleRenderer"/> /// <see cref="Rendering.SimpleRenderer"/>
Simple = 0, Simple = 0,
/// <see cref="Rendering.SplatoonRenderer"/> /// <see cref="Rendering.SplatoonRenderer"/>
Splatoon = 1, Splatoon = 1,
}
} }

View File

@ -4,21 +4,22 @@ using System.Numerics;
using ImGuiNET; using ImGuiNET;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Pal.Client.Configuration namespace Pal.Client.Configuration;
{
public interface IVersioned
{
int Version { get; set; }
}
public interface IConfigurationInConfigDirectory : IVersioned
{
}
public interface IPalacePalConfiguration : IConfigurationInConfigDirectory public interface IVersioned
{ {
int Version { get; set; }
}
public interface IConfigurationInConfigDirectory : IVersioned
{
}
public interface IPalacePalConfiguration : IConfigurationInConfigDirectory
{
bool FirstUse { get; set; } bool FirstUse { get; set; }
EMode Mode { get; set; } EMode Mode { get; set; }
string BetaKey { get; } string BetaKey { get; }
bool HasBetaFeature(string feature) => BetaKey.Contains(feature);
DeepDungeonConfiguration DeepDungeons { get; set; } DeepDungeonConfiguration DeepDungeons { get; set; }
RendererConfiguration Renderer { get; set; } RendererConfiguration Renderer { get; set; }
@ -29,10 +30,10 @@ namespace Pal.Client.Configuration
void RemoveAccount(string server); void RemoveAccount(string server);
bool HasRoleOnCurrentServer(string server, string role); bool HasRoleOnCurrentServer(string server, string role);
} }
public class DeepDungeonConfiguration public class DeepDungeonConfiguration
{ {
public MarkerConfiguration Traps { get; set; } = new() public MarkerConfiguration Traps { get; set; } = new()
{ {
Show = true, Show = true,
@ -56,10 +57,18 @@ namespace Pal.Client.Configuration
OnlyVisibleAfterPomander = false, OnlyVisibleAfterPomander = false,
Fill = true Fill = true
}; };
}
public class MarkerConfiguration public MarkerConfiguration GoldCoffers { get; set; } = new()
{ {
Show = false,
Color = ImGui.ColorConvertFloat4ToU32(new Vector4(1, 1, 0, 0.4f)),
OnlyVisibleAfterPomander = false,
Fill = true
};
}
public class MarkerConfiguration
{
[JsonRequired] [JsonRequired]
public bool Show { get; set; } public bool Show { get; set; }
@ -68,15 +77,15 @@ namespace Pal.Client.Configuration
public bool OnlyVisibleAfterPomander { get; set; } public bool OnlyVisibleAfterPomander { get; set; }
public bool Fill { get; set; } public bool Fill { get; set; }
} }
public class RendererConfiguration public class RendererConfiguration
{ {
public ERenderer SelectedRenderer { get; set; } = ERenderer.Splatoon; public ERenderer SelectedRenderer { get; set; } = ERenderer.Splatoon;
} }
public interface IAccountConfiguration public interface IAccountConfiguration
{ {
bool IsUsable { get; } bool IsUsable { get; }
string Server { get; } string Server { get; }
Guid AccountId { get; } Guid AccountId { get; }
@ -92,11 +101,10 @@ namespace Pal.Client.Configuration
List<string> CachedRoles { get; set; } List<string> CachedRoles { get; set; }
bool EncryptIfNeeded(); bool EncryptIfNeeded();
} }
public class BackupConfiguration public class BackupConfiguration
{ {
public int MinimumBackupsToKeep { get; set; } = 3; public int MinimumBackupsToKeep { get; set; } = 3;
public int DaysToDeleteAfter { get; set; } = 21; public int DaysToDeleteAfter { get; set; } = 21;
}
} }

View File

@ -8,11 +8,11 @@ using Dalamud.Plugin;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace Pal.Client.Configuration.Legacy namespace Pal.Client.Configuration.Legacy;
[Obsolete]
public sealed class ConfigurationV1
{ {
[Obsolete]
public sealed class ConfigurationV1
{
public int Version { get; set; } = 6; public int Version { get; set; } = 6;
#region Saved configuration values #region Saved configuration values
@ -50,7 +50,7 @@ namespace Pal.Client.Configuration.Legacy
public string BetaKey { get; set; } = ""; public string BetaKey { get; set; } = "";
#endregion #endregion
public void Migrate(DalamudPluginInterface pluginInterface, ILogger<ConfigurationV1> logger) public void Migrate(IDalamudPluginInterface pluginInterface, ILogger<ConfigurationV1> logger)
{ {
if (Version == 1) if (Version == 1)
{ {
@ -137,7 +137,7 @@ namespace Pal.Client.Configuration.Legacy
} }
} }
public void Save(DalamudPluginInterface pluginInterface) public void Save(IDalamudPluginInterface pluginInterface)
{ {
File.WriteAllText(pluginInterface.ConfigFile.FullName, JsonConvert.SerializeObject(this, Formatting.Indented, new JsonSerializerSettings File.WriteAllText(pluginInterface.ConfigFile.FullName, JsonConvert.SerializeObject(this, Formatting.Indented, new JsonSerializerSettings
{ {
@ -163,5 +163,4 @@ namespace Pal.Client.Configuration.Legacy
/// </summary> /// </summary>
public DateTime ImportedAt { get; set; } public DateTime ImportedAt { get; set; }
} }
}
} }

View File

@ -7,19 +7,18 @@ using System.Text.Json;
using Pal.Client.Extensions; using Pal.Client.Extensions;
using Pal.Common; using Pal.Common;
namespace Pal.Client.Configuration.Legacy namespace Pal.Client.Configuration.Legacy;
/// <summary>
/// Legacy JSON file for marker locations.
/// </summary>
[Obsolete]
public sealed class JsonFloorState
{ {
/// <summary>
/// Legacy JSON file for marker locations.
/// </summary>
[Obsolete]
public sealed class JsonFloorState
{
private static readonly JsonSerializerOptions JsonSerializerOptions = new() { IncludeFields = true }; private static readonly JsonSerializerOptions JsonSerializerOptions = new() { IncludeFields = true };
private const int CurrentVersion = 4; private const int CurrentVersion = 4;
private static string _pluginConfigDirectory = null!; private static string _pluginConfigDirectory = null!;
private static readonly EMode _mode = EMode.Online; // might not be true, but this is 'less strict filtering' for migrations
internal static void SetContextProperties(string pluginConfigDirectory) internal static void SetContextProperties(string pluginConfigDirectory)
{ {
@ -36,11 +35,6 @@ namespace Pal.Client.Configuration.Legacy
private void ApplyFilters() private void ApplyFilters()
{ {
if (_mode == EMode.Offline)
Markers = new ConcurrentBag<JsonMarker>(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<JsonMarker>(Markers.Where(x => x.Seen || !x.WasImported || x.Imports.Count > 0)); Markers = new ConcurrentBag<JsonMarker>(Markers.Where(x => x.Seen || !x.WasImported || x.Imports.Count > 0));
} }
@ -164,5 +158,4 @@ namespace Pal.Client.Configuration.Legacy
public int Version { get; set; } public int Version { get; set; }
public HashSet<JsonMarker> Markers { get; set; } = new(); public HashSet<JsonMarker> Markers { get; set; } = new();
} }
}
} }

View File

@ -2,11 +2,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
namespace Pal.Client.Configuration.Legacy namespace Pal.Client.Configuration.Legacy;
[Obsolete]
public class JsonMarker
{ {
[Obsolete]
public class JsonMarker
{
public EType Type { get; set; } = EType.Unknown; public EType Type { get; set; } = EType.Unknown;
public Vector3 Position { get; set; } public Vector3 Position { get; set; }
public bool Seen { get; set; } public bool Seen { get; set; }
@ -22,5 +22,4 @@ namespace Pal.Client.Configuration.Legacy
Hoard = 2, Hoard = 2,
Debug = 3, Debug = 3,
} }
}
} }

View File

@ -12,19 +12,19 @@ using Microsoft.Extensions.Logging;
using Pal.Client.Database; using Pal.Client.Database;
using Pal.Common; using Pal.Common;
namespace Pal.Client.Configuration.Legacy namespace Pal.Client.Configuration.Legacy;
/// <summary>
/// Imports legacy territoryType.json files into the database if it exists, and no markers for that territory exist.
/// </summary>
internal sealed class JsonMigration
{ {
/// <summary>
/// Imports legacy territoryType.json files into the database if it exists, and no markers for that territory exist.
/// </summary>
internal sealed class JsonMigration
{
private readonly ILogger<JsonMigration> _logger; private readonly ILogger<JsonMigration> _logger;
private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly DalamudPluginInterface _pluginInterface; private readonly IDalamudPluginInterface _pluginInterface;
public JsonMigration(ILogger<JsonMigration> logger, IServiceScopeFactory serviceScopeFactory, public JsonMigration(ILogger<JsonMigration> logger, IServiceScopeFactory serviceScopeFactory,
DalamudPluginInterface pluginInterface) IDalamudPluginInterface pluginInterface)
{ {
_logger = logger; _logger = logger;
_serviceScopeFactory = serviceScopeFactory; _serviceScopeFactory = serviceScopeFactory;
@ -143,5 +143,4 @@ namespace Pal.Client.Configuration.Legacy
}; };
} }
#pragma warning restore CS0612 #pragma warning restore CS0612
}
} }

View File

@ -6,10 +6,10 @@ using Microsoft.Extensions.Logging;
using Pal.Client.Configuration; using Pal.Client.Configuration;
using Pal.Common; using Pal.Common;
namespace Pal.Client.Database namespace Pal.Client.Database;
internal sealed class Cleanup
{ {
internal sealed class Cleanup
{
private readonly ILogger<Cleanup> _logger; private readonly ILogger<Cleanup> _logger;
private readonly IPalacePalConfiguration _configuration; private readonly IPalacePalConfiguration _configuration;
@ -63,5 +63,4 @@ namespace Pal.Client.Database
// keep downloaded markers // keep downloaded markers
return o => o.Source != ClientLocation.ESource.Download; return o => o.Source != ClientLocation.ESource.Download;
} }
}
} }

View File

@ -1,10 +1,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
namespace Pal.Client.Database namespace Pal.Client.Database;
internal sealed class ClientLocation
{ {
internal sealed class ClientLocation
{
[Key] public int LocalId { get; set; } [Key] public int LocalId { get; set; }
public ushort TerritoryType { get; set; } public ushort TerritoryType { get; set; }
public EType Type { get; set; } public EType Type { get; set; }
@ -55,5 +55,4 @@ namespace Pal.Client.Database
Import = 3, Import = 3,
Download = 4, Download = 4,
} }
}
} }

View File

@ -1,15 +1,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Pal.Client.Database namespace Pal.Client.Database;
internal sealed class ImportHistory
{ {
internal sealed class ImportHistory
{
public Guid Id { get; set; } public Guid Id { get; set; }
public string? RemoteUrl { get; set; } public string? RemoteUrl { get; set; }
public DateTime ExportedAt { get; set; } public DateTime ExportedAt { get; set; }
public DateTime ImportedAt { get; set; } public DateTime ImportedAt { get; set; }
public List<ClientLocation> ImportedLocations { get; set; } = new(); public List<ClientLocation> ImportedLocations { get; set; } = new();
}
} }

View File

@ -1,9 +1,9 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Pal.Client.Database namespace Pal.Client.Database;
internal class PalClientContext : DbContext
{ {
internal class PalClientContext : DbContext
{
public DbSet<ClientLocation> Locations { get; set; } = null!; public DbSet<ClientLocation> Locations { get; set; } = null!;
public DbSet<ImportHistory> Imports { get; set; } = null!; public DbSet<ImportHistory> Imports { get; set; } = null!;
public DbSet<RemoteEncounter> RemoteEncounters { get; set; } = null!; public DbSet<RemoteEncounter> RemoteEncounters { get; set; } = null!;
@ -20,5 +20,4 @@ namespace Pal.Client.Database
.WithMany(o => o.ImportedLocations) .WithMany(o => o.ImportedLocations)
.UsingEntity(o => o.ToTable("LocationImports")); .UsingEntity(o => o.ToTable("LocationImports"));
} }
}
} }

View File

@ -2,17 +2,17 @@
using Pal.Client.Extensions; using Pal.Client.Extensions;
using Pal.Client.Net; using Pal.Client.Net;
namespace Pal.Client.Database namespace Pal.Client.Database;
/// <summary>
/// To avoid sending too many requests to the server, we cache which locations have been seen
/// locally. These never expire, and locations which have been seen with a specific account
/// are never sent to the server again.
///
/// To be marked as seen, it needs to be essentially processed by <see cref="RemoteApi.MarkAsSeen"/>.
/// </summary>
internal sealed class RemoteEncounter
{ {
/// <summary>
/// To avoid sending too many requests to the server, we cache which locations have been seen
/// locally. These never expire, and locations which have been seen with a specific account
/// are never sent to the server again.
///
/// To be marked as seen, it needs to be essentially processed by <see cref="RemoteApi.MarkAsSeen"/>.
/// </summary>
internal sealed class RemoteEncounter
{
[Key] [Key]
public int Id { get; private set; } public int Id { get; private set; }
@ -37,5 +37,4 @@ namespace Pal.Client.Database
ClientLocation = clientLocation; ClientLocation = clientLocation;
AccountId = accountId.ToPartialId(); AccountId = accountId.ToPartialId();
} }
}
} }

View File

@ -19,14 +19,14 @@ using Pal.Client.DependencyInjection;
using Pal.Client.Floors; using Pal.Client.Floors;
using Pal.Client.Windows; using Pal.Client.Windows;
namespace Pal.Client namespace Pal.Client;
/// <summary>
/// Takes care of async plugin init - this is mostly everything that requires either the config or the database to
/// be available.
/// </summary>
internal sealed class DependencyContextInitializer
{ {
/// <summary>
/// Takes care of async plugin init - this is mostly everything that requires either the config or the database to
/// be available.
/// </summary>
internal sealed class DependencyContextInitializer
{
private readonly ILogger<DependencyContextInitializer> _logger; private readonly ILogger<DependencyContextInitializer> _logger;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
@ -69,10 +69,12 @@ namespace Pal.Client
_serviceProvider.GetRequiredService<ChatService>(); _serviceProvider.GetRequiredService<ChatService>();
// eager load any commands to find errors now, not when running them // eager load any commands to find errors now, not when running them
_serviceProvider.GetRequiredService<PalConfigCommand>(); _serviceProvider.GetRequiredService<IEnumerable<ISubCommand>>();
_serviceProvider.GetRequiredService<PalNearCommand>();
_serviceProvider.GetRequiredService<PalStatsCommand>(); cancellationToken.ThrowIfCancellationRequested();
_serviceProvider.GetRequiredService<PalTestConnectionCommand>();
if (_serviceProvider.GetRequiredService<IPalacePalConfiguration>().HasBetaFeature(ObjectTableDebug.FeatureName))
_serviceProvider.GetRequiredService<ObjectTableDebug>();
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
@ -82,7 +84,7 @@ namespace Pal.Client
private async Task RemoveOldBackups() private async Task RemoveOldBackups()
{ {
await using var scope = _serviceProvider.CreateAsyncScope(); await using var scope = _serviceProvider.CreateAsyncScope();
var pluginInterface = scope.ServiceProvider.GetRequiredService<DalamudPluginInterface>(); var pluginInterface = scope.ServiceProvider.GetRequiredService<IDalamudPluginInterface>();
var configuration = scope.ServiceProvider.GetRequiredService<IPalacePalConfiguration>(); var configuration = scope.ServiceProvider.GetRequiredService<IPalacePalConfiguration>();
var paths = Directory.GetFiles(pluginInterface.GetPluginConfigDirectory(), "backup-*.data.sqlite3", var paths = Directory.GetFiles(pluginInterface.GetPluginConfigDirectory(), "backup-*.data.sqlite3",
@ -134,7 +136,7 @@ namespace Pal.Client
{ {
await using var scope = _serviceProvider.CreateAsyncScope(); await using var scope = _serviceProvider.CreateAsyncScope();
var pluginInterface = scope.ServiceProvider.GetRequiredService<DalamudPluginInterface>(); var pluginInterface = scope.ServiceProvider.GetRequiredService<IDalamudPluginInterface>();
string backupPath = Path.Join(pluginInterface.GetPluginConfigDirectory(), string backupPath = Path.Join(pluginInterface.GetPluginConfigDirectory(),
$"backup-{DateTime.Now.ToUniversalTime():yyyy-MM-dd}.data.sqlite3"); $"backup-{DateTime.Now.ToUniversalTime():yyyy-MM-dd}.data.sqlite3");
string sourcePath = Path.Join(pluginInterface.GetPluginConfigDirectory(), string sourcePath = Path.Join(pluginInterface.GetPluginConfigDirectory(),
@ -190,5 +192,4 @@ namespace Pal.Client
await dbContext.SaveChangesAsync(); await dbContext.SaveChangesAsync();
} }
}
} }

View File

@ -1,15 +1,16 @@
using Dalamud.Game.Gui; using Dalamud.Game.Text;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services;
using ECommons.DalamudServices.Legacy;
using Pal.Client.Properties; using Pal.Client.Properties;
namespace Pal.Client.DependencyInjection namespace Pal.Client.DependencyInjection;
{
internal sealed class Chat
{
private readonly ChatGui _chatGui;
public Chat(ChatGui chatGui) internal sealed class Chat
{
private readonly IChatGui _chatGui;
public Chat(IChatGui chatGui)
{ {
_chatGui = chatGui; _chatGui = chatGui;
} }
@ -34,5 +35,4 @@ namespace Pal.Client.DependencyInjection
public void UnformattedMessage(string message) public void UnformattedMessage(string message)
=> _chatGui.Print(message); => _chatGui.Print(message);
}
} }

View File

@ -1,25 +1,25 @@
using System; using System;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Dalamud.Data;
using Dalamud.Game.Gui;
using Dalamud.Game.Text; using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using Pal.Client.Configuration; using Pal.Client.Configuration;
using Pal.Client.Floors; using Pal.Client.Floors;
namespace Pal.Client.DependencyInjection namespace Pal.Client.DependencyInjection;
internal sealed class ChatService : IDisposable
{ {
internal sealed class ChatService : IDisposable private readonly IChatGui _chatGui;
{
private readonly ChatGui _chatGui;
private readonly TerritoryState _territoryState; private readonly TerritoryState _territoryState;
private readonly IPalacePalConfiguration _configuration; private readonly IPalacePalConfiguration _configuration;
private readonly DataManager _dataManager; private readonly IDataManager _dataManager;
private readonly LocalizedChatMessages _localizedChatMessages; private readonly LocalizedChatMessages _localizedChatMessages;
public ChatService(ChatGui chatGui, TerritoryState territoryState, IPalacePalConfiguration configuration, public ChatService(IChatGui chatGui, TerritoryState territoryState, IPalacePalConfiguration configuration,
DataManager dataManager) IDataManager dataManager)
{ {
_chatGui = chatGui; _chatGui = chatGui;
_territoryState = territoryState; _territoryState = territoryState;
@ -34,7 +34,7 @@ namespace Pal.Client.DependencyInjection
public void Dispose() public void Dispose()
=> _chatGui.ChatMessage -= OnChatMessage; => _chatGui.ChatMessage -= OnChatMessage;
private void OnChatMessage(XivChatType type, uint senderId, ref SeString sender, ref SeString seMessage, private void OnChatMessage(XivChatType type, int senderId, ref SeString sender, ref SeString seMessage,
ref bool isHandled) ref bool isHandled)
{ {
if (_configuration.FirstUse) if (_configuration.FirstUse)
@ -82,14 +82,21 @@ namespace Pal.Client.DependencyInjection
HoardNotOnCurrentFloor = GetLocalizedString(7273), HoardNotOnCurrentFloor = GetLocalizedString(7273),
HoardCofferOpened = GetLocalizedString(7274), HoardCofferOpened = GetLocalizedString(7274),
FloorChanged = FloorChanged =
new Regex("^" + GetLocalizedString(7270).Replace("\u0002 \u0003\ufffd\u0002\u0003", @"(\d+)") + new Regex("^" + GetLocalizedString(7270, true).Replace("\u0002 \u0003\ufffd\u0002\u0003", @"(\d+)") +
"$"), "$"),
}; };
} }
private string GetLocalizedString(uint id) private string GetLocalizedString(uint id, bool asRawData = false)
{ {
return _dataManager.GetExcelSheet<LogMessage>()?.GetRow(id)?.Text?.ToString() ?? "Unknown"; var text = _dataManager.GetExcelSheet<LogMessage>()?.GetRow(id)?.Text;
if (text == null)
return "Unknown";
if (asRawData)
return Encoding.UTF8.GetString(text.RawData);
else
return text.ToString();
} }
private sealed class LocalizedChatMessages private sealed class LocalizedChatMessages
@ -106,5 +113,4 @@ namespace Pal.Client.DependencyInjection
public Regex FloorChanged { get; init; } = public Regex FloorChanged { get; init; } =
new(@"This isn't a game message, but will be replaced"); // new Regex(@"^Floor (\d+)$"); new(@"This isn't a game message, but will be replaced"); // new Regex(@"^Floor (\d+)$");
} }
}
} }

View File

@ -1,9 +1,9 @@
using System; using System;
namespace Pal.Client.DependencyInjection namespace Pal.Client.DependencyInjection;
internal sealed class DebugState
{ {
internal sealed class DebugState
{
public string? DebugMessage { get; set; } public string? DebugMessage { get; set; }
public void SetFromException(Exception e) public void SetFromException(Exception e)
@ -11,5 +11,4 @@ namespace Pal.Client.DependencyInjection
public void Reset() public void Reset()
=> DebugMessage = null; => DebugMessage = null;
}
} }

View File

@ -4,16 +4,17 @@ using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Hooking; using Dalamud.Hooking;
using Dalamud.Memory; using Dalamud.Memory;
using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures; using Dalamud.Utility.Signatures;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Pal.Client.Floors; using Pal.Client.Floors;
namespace Pal.Client.DependencyInjection namespace Pal.Client.DependencyInjection;
internal sealed unsafe class GameHooks : IDisposable
{ {
internal sealed unsafe class GameHooks : IDisposable
{
private readonly ILogger<GameHooks> _logger; private readonly ILogger<GameHooks> _logger;
private readonly ObjectTable _objectTable; private readonly IObjectTable _objectTable;
private readonly TerritoryState _territoryState; private readonly TerritoryState _territoryState;
private readonly FrameworkService _frameworkService; private readonly FrameworkService _frameworkService;
@ -24,7 +25,7 @@ namespace Pal.Client.DependencyInjection
private Hook<ActorVfxCreateDelegate> ActorVfxCreateHook { get; init; } = null!; private Hook<ActorVfxCreateDelegate> ActorVfxCreateHook { get; init; } = null!;
#pragma warning restore CS0649 #pragma warning restore CS0649
public GameHooks(ILogger<GameHooks> logger, ObjectTable objectTable, TerritoryState territoryState, FrameworkService frameworkService) public GameHooks(ILogger<GameHooks> logger, IObjectTable objectTable, TerritoryState territoryState, FrameworkService frameworkService, IGameInteropProvider gameInteropProvider)
{ {
_logger = logger; _logger = logger;
_objectTable = objectTable; _objectTable = objectTable;
@ -32,7 +33,7 @@ namespace Pal.Client.DependencyInjection
_frameworkService = frameworkService; _frameworkService = frameworkService;
_logger.LogDebug("Initializing game hooks"); _logger.LogDebug("Initializing game hooks");
SignatureHelper.Initialise(this); gameInteropProvider.InitializeFromAttributes(this);
ActorVfxCreateHook.Enable(); ActorVfxCreateHook.Enable();
_logger.LogDebug("Game hooks initialized"); _logger.LogDebug("Game hooks initialized");
@ -80,7 +81,7 @@ namespace Pal.Client.DependencyInjection
_chat.PalPrint($"{vfxPath} on {obj}"); _chat.PalPrint($"{vfxPath} on {obj}");
*/ */
if (obj is BattleChara bc && (bc.NameId == /* potd */ 5042 || bc.NameId == /* hoh */ 7395)) if (obj is IBattleChara bc && (bc.NameId == /* potd */ 5042 || bc.NameId == /* hoh */ 7395))
{ {
if (vfxPath == "vfx/common/eff/dk05th_stdn0t.avfx" || vfxPath == "vfx/common/eff/dk05ht_ipws0t.avfx") if (vfxPath == "vfx/common/eff/dk05th_stdn0t.avfx" || vfxPath == "vfx/common/eff/dk05ht_ipws0t.avfx")
{ {
@ -102,5 +103,4 @@ namespace Pal.Client.DependencyInjection
_logger.LogDebug("Disposing game hooks"); _logger.LogDebug("Disposing game hooks");
ActorVfxCreateHook.Dispose(); ActorVfxCreateHook.Dispose();
} }
}
} }

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Numerics; using System.Numerics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Account; using Export;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Pal.Client.Database; using Pal.Client.Database;
@ -12,10 +12,10 @@ using Pal.Client.Floors;
using Pal.Client.Floors.Tasks; using Pal.Client.Floors.Tasks;
using Pal.Common; 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; private readonly FloorService _floorService;
private readonly Cleanup _cleanup; private readonly Cleanup _cleanup;
@ -162,5 +162,4 @@ namespace Pal.Client.DependencyInjection
_floorService.ResetAll(); _floorService.ResetAll();
} }
} }
}
} }

View File

@ -1,90 +0,0 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
using Serilog.Events;
namespace Pal.Client.DependencyInjection.Logging
{
internal sealed class DalamudLogger : ILogger
{
private static readonly string AssemblyName = typeof(Plugin).Assembly.GetName().Name!;
private static readonly Serilog.ILogger PluginLogDelegate = Serilog.Log.ForContext("SourceContext", AssemblyName);
private readonly string _name;
private readonly IExternalScopeProvider? _scopeProvider;
public DalamudLogger(string name, IExternalScopeProvider? scopeProvider)
{
_name = name;
_scopeProvider = scopeProvider;
}
public IDisposable BeginScope<TState>(TState state)
where TState : notnull
=> _scopeProvider?.Push(state) ?? NullScope.Instance;
public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None && IsEnabled(ToSerilogLevel(logLevel));
private bool IsEnabled(LogEventLevel logEventLevel) => PluginLogDelegate.IsEnabled(logEventLevel);
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception,
Func<TState, Exception?, string> formatter)
{
if (logLevel == LogLevel.None)
return;
LogEventLevel logEventLevel = ToSerilogLevel(logLevel);
if (!IsEnabled(logEventLevel))
return;
if (formatter == null)
throw new ArgumentNullException(nameof(formatter));
StringBuilder sb = new StringBuilder();
sb.Append('[').Append(AssemblyName).Append("] ");
_scopeProvider?.ForEachScope((scope, builder) =>
{
if (scope is IEnumerable<KeyValuePair<string, object>> properties)
{
foreach (KeyValuePair<string, object> pair in properties)
{
builder.Append('<').Append(pair.Key).Append('=').Append(pair.Value)
.Append("> ");
}
}
else if (scope != null)
builder.Append('<').Append(scope).Append("> ");
},
sb);
sb.Append(_name).Append(": ").Append(formatter(state, null));
PluginLogDelegate.Write(logEventLevel, exception, sb.ToString());
}
private LogEventLevel ToSerilogLevel(LogLevel logLevel)
{
return logLevel switch
{
LogLevel.Critical => LogEventLevel.Fatal,
LogLevel.Error => LogEventLevel.Error,
LogLevel.Warning => LogEventLevel.Warning,
LogLevel.Information => LogEventLevel.Information,
LogLevel.Debug => LogEventLevel.Debug,
LogLevel.Trace => LogEventLevel.Verbose,
_ => throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null)
};
}
private sealed class NullScope : IDisposable
{
public static NullScope Instance { get; } = new();
private NullScope()
{
}
public void Dispose()
{
}
}
}
}

View File

@ -1,31 +0,0 @@
using Microsoft.Extensions.Logging;
using System;
namespace Pal.Client.DependencyInjection.Logging
{
internal sealed class DalamudLoggerProvider : ILoggerProvider, ISupportExternalScope
{
private IExternalScopeProvider? _scopeProvider;
public ILogger CreateLogger(string categoryName) => new DalamudLogger(categoryName, _scopeProvider);
/// <summary>
/// Manual logger creation, doesn't handle scopes.
/// </summary>
public ILogger CreateLogger(Type type) => CreateLogger(type.FullName ?? type.ToString());
/// <summary>
/// Manual logger creation, doesn't handle scopes.
/// </summary>
public ILogger CreateLogger<T>() => CreateLogger(typeof(T));
public void SetScopeProvider(IExternalScopeProvider scopeProvider)
{
_scopeProvider = scopeProvider;
}
public void Dispose()
{
}
}
}

View File

@ -6,21 +6,22 @@ using Microsoft.Extensions.Logging;
using Pal.Client.Extensions; using Pal.Client.Extensions;
using Pal.Client.Properties; using Pal.Client.Properties;
namespace Pal.Client.DependencyInjection namespace Pal.Client.DependencyInjection;
internal sealed class RepoVerification
{ {
internal sealed class RepoVerification public RepoVerification(ILogger<RepoVerification> logger, IDalamudPluginInterface pluginInterface, Chat chat)
{
public RepoVerification(ILogger<RepoVerification> logger, DalamudPluginInterface pluginInterface, Chat chat)
{ {
logger.LogInformation("Install source: {Repo}", pluginInterface.SourceRepository); logger.LogInformation("Install source: {Repo}", pluginInterface.SourceRepository);
if (!pluginInterface.IsDev if (!pluginInterface.IsDev && pluginInterface.SourceRepository.TrimEnd('/') != "https://plugins.carvel.li")
&& !pluginInterface.SourceRepository.StartsWith("https://raw.githubusercontent.com/carvelli/")
&& !pluginInterface.SourceRepository.StartsWith("https://github.com/carvelli/"))
{ {
chat.Error(string.Format(Localization.Error_WrongRepository, chat.Error(string.Format(Localization.Error_WrongRepository,
"https://github.com/carvelli/Dalamud-Plugins")); "https://plugins.carvel.li"));
throw new InvalidOperationException(); throw new RepoVerificationFailedException();
} }
} }
internal sealed class RepoVerificationFailedException : Exception
{
} }
} }

View File

@ -2,25 +2,32 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Dalamud.Game.Gui; using Dalamud.Game.Gui;
using Grpc.Core; using Grpc.Core;
using Microsoft.Extensions.Logging;
using Pal.Client.Configuration; using Pal.Client.Configuration;
using Pal.Client.Extensions; using Pal.Client.Extensions;
using Pal.Client.Net; using Pal.Client.Net;
using Pal.Client.Properties; using Pal.Client.Properties;
using Pal.Client.Windows; using Pal.Client.Windows;
namespace Pal.Client.DependencyInjection namespace Pal.Client.DependencyInjection;
internal sealed class StatisticsService
{ {
internal sealed class StatisticsService
{
private readonly IPalacePalConfiguration _configuration; private readonly IPalacePalConfiguration _configuration;
private readonly ILogger<StatisticsService> _logger;
private readonly RemoteApi _remoteApi; private readonly RemoteApi _remoteApi;
private readonly StatisticsWindow _statisticsWindow; private readonly StatisticsWindow _statisticsWindow;
private readonly Chat _chat; private readonly Chat _chat;
public StatisticsService(IPalacePalConfiguration configuration, RemoteApi remoteApi, public StatisticsService(
StatisticsWindow statisticsWindow, Chat chat) IPalacePalConfiguration configuration,
ILogger<StatisticsService> logger,
RemoteApi remoteApi,
StatisticsWindow statisticsWindow,
Chat chat)
{ {
_configuration = configuration; _configuration = configuration;
_logger = logger;
_remoteApi = remoteApi; _remoteApi = remoteApi;
_statisticsWindow = statisticsWindow; _statisticsWindow = statisticsWindow;
_chat = chat; _chat = chat;
@ -54,12 +61,14 @@ namespace Pal.Client.DependencyInjection
} }
catch (RpcException e) when (e.StatusCode == StatusCode.PermissionDenied) catch (RpcException e) when (e.StatusCode == StatusCode.PermissionDenied)
{ {
_logger.LogWarning(e, "Access denied while fetching floor statistics");
_chat.Error(Localization.Command_pal_stats_CurrentFloor); _chat.Error(Localization.Command_pal_stats_CurrentFloor);
} }
catch (Exception e) catch (Exception e)
{ {
_chat.Error(e.ToString()); _logger.LogError(e, "Could not fetch floor statistics");
} _chat.Error(string.Format(Localization.Error_CommandFailed,
$"{e.GetType()} - {e.Message}"));
} }
} }
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using Dalamud.Data; using Dalamud.Data;
using Dalamud.Extensions.MicrosoftLogging;
using Dalamud.Game; using Dalamud.Game;
using Dalamud.Game.ClientState; using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Conditions;
@ -9,6 +10,7 @@ using Dalamud.Game.Command;
using Dalamud.Game.Gui; using Dalamud.Game.Gui;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -18,47 +20,46 @@ using Pal.Client.Configuration;
using Pal.Client.Configuration.Legacy; using Pal.Client.Configuration.Legacy;
using Pal.Client.Database; using Pal.Client.Database;
using Pal.Client.DependencyInjection; using Pal.Client.DependencyInjection;
using Pal.Client.DependencyInjection.Logging;
using Pal.Client.Floors; using Pal.Client.Floors;
using Pal.Client.Net; using Pal.Client.Net;
using Pal.Client.Properties;
using Pal.Client.Rendering; using Pal.Client.Rendering;
using Pal.Client.Scheduled; using Pal.Client.Scheduled;
using Pal.Client.Windows; using Pal.Client.Windows;
namespace Pal.Client namespace Pal.Client;
/// <summary>
/// DI-aware Plugin.
/// </summary>
internal sealed class DependencyInjectionContext : IDisposable
{ {
/// <summary>
/// DI-aware Plugin.
/// </summary>
internal sealed class DependencyInjectionContext : IDisposable
{
public const string DatabaseFileName = "palace-pal.data.sqlite3"; public const string DatabaseFileName = "palace-pal.data.sqlite3";
public static DalamudLoggerProvider LoggerProvider { get; } = new();
/// <summary> /// <summary>
/// Initialized as temporary logger, will be overriden once context is ready with a logger that supports scopes. /// Initialized as temporary logger, will be overriden once context is ready with a logger that supports scopes.
/// </summary> /// </summary>
private ILogger _logger = LoggerProvider.CreateLogger<DependencyInjectionContext>(); private ILogger _logger;
private readonly string _sqliteConnectionString; private readonly string _sqliteConnectionString;
private readonly ServiceCollection _serviceCollection = new(); private readonly ServiceCollection _serviceCollection = new();
private ServiceProvider? _serviceProvider; private ServiceProvider? _serviceProvider;
public string Name => Localization.Palace_Pal;
public DependencyInjectionContext( public DependencyInjectionContext(
DalamudPluginInterface pluginInterface, IDalamudPluginInterface pluginInterface,
ClientState clientState, IClientState clientState,
GameGui gameGui, IGameGui gameGui,
ChatGui chatGui, IChatGui chatGui,
ObjectTable objectTable, IObjectTable objectTable,
Framework framework, IFramework framework,
Condition condition, ICondition condition,
CommandManager commandManager, ICommandManager commandManager,
DataManager dataManager, IDataManager dataManager,
IGameInteropProvider gameInteropProvider,
IPluginLog pluginLog,
Plugin plugin) Plugin plugin)
{ {
var loggerProvider = new DalamudLoggerProvider(pluginLog);
_logger = loggerProvider.CreateLogger<DependencyInjectionContext>();
_logger.LogInformation("Building dalamud service container for {Assembly}", _logger.LogInformation("Building dalamud service container for {Assembly}",
typeof(DependencyInjectionContext).Assembly.FullName); typeof(DependencyInjectionContext).Assembly.FullName);
@ -73,7 +74,7 @@ namespace Pal.Client
.AddFilter("Microsoft.EntityFrameworkCore.Database", LogLevel.Warning) .AddFilter("Microsoft.EntityFrameworkCore.Database", LogLevel.Warning)
.AddFilter("Grpc", LogLevel.Debug) .AddFilter("Grpc", LogLevel.Debug)
.ClearProviders() .ClearProviders()
.AddProvider(LoggerProvider)); .AddDalamudLogger(pluginLog));
// dalamud // dalamud
_serviceCollection.AddSingleton<IDalamudPlugin>(plugin); _serviceCollection.AddSingleton<IDalamudPlugin>(plugin);
@ -87,6 +88,7 @@ namespace Pal.Client
_serviceCollection.AddSingleton(condition); _serviceCollection.AddSingleton(condition);
_serviceCollection.AddSingleton(commandManager); _serviceCollection.AddSingleton(commandManager);
_serviceCollection.AddSingleton(dataManager); _serviceCollection.AddSingleton(dataManager);
_serviceCollection.AddSingleton(gameInteropProvider);
_serviceCollection.AddSingleton(new WindowSystem(typeof(DependencyInjectionContext).AssemblyQualifiedName)); _serviceCollection.AddSingleton(new WindowSystem(typeof(DependencyInjectionContext).AssemblyQualifiedName));
_sqliteConnectionString = _sqliteConnectionString =
@ -117,9 +119,10 @@ namespace Pal.Client
// commands // commands
_serviceCollection.AddScoped<PalConfigCommand>(); _serviceCollection.AddScoped<PalConfigCommand>();
_serviceCollection.AddScoped<PalNearCommand>(); _serviceCollection.AddScoped<ISubCommand, PalConfigCommand>();
_serviceCollection.AddScoped<PalStatsCommand>(); _serviceCollection.AddScoped<ISubCommand, PalNearCommand>();
_serviceCollection.AddScoped<PalTestConnectionCommand>(); _serviceCollection.AddScoped<ISubCommand, PalStatsCommand>();
_serviceCollection.AddScoped<ISubCommand, PalTestConnectionCommand>();
// territory & marker related services // territory & marker related services
_serviceCollection.AddScoped<TerritoryState>(); _serviceCollection.AddScoped<TerritoryState>();
@ -127,6 +130,7 @@ namespace Pal.Client
_serviceCollection.AddScoped<ChatService>(); _serviceCollection.AddScoped<ChatService>();
_serviceCollection.AddScoped<FloorService>(); _serviceCollection.AddScoped<FloorService>();
_serviceCollection.AddScoped<ImportService>(); _serviceCollection.AddScoped<ImportService>();
_serviceCollection.AddScoped<ObjectTableDebug>();
// windows & related services // windows & related services
_serviceCollection.AddScoped<AgreementWindow>(); _serviceCollection.AddScoped<AgreementWindow>();
@ -187,5 +191,4 @@ namespace Pal.Client
using (SqliteConnection sqliteConnection = new(_sqliteConnectionString)) using (SqliteConnection sqliteConnection = new(_sqliteConnectionString))
SqliteConnection.ClearPool(sqliteConnection); SqliteConnection.ClearPool(sqliteConnection);
} }
}
} }

View File

@ -1,13 +1,12 @@
using System; using System;
namespace Pal.Client.Extensions namespace Pal.Client.Extensions;
public static class GuidExtensions
{ {
public static class GuidExtensions
{
public static string ToPartialId(this Guid g, int length = 13) public static string ToPartialId(this Guid g, int length = 13)
=> g.ToString().ToPartialId(); => g.ToString().ToPartialId();
public static string ToPartialId(this string s, int length = 13) public static string ToPartialId(this string s, int length = 13)
=> s.PadRight(length + 1).Substring(0, length); => s.PadRight(length + 1).Substring(0, length);
}
} }

View File

@ -3,10 +3,10 @@ using System.Runtime.InteropServices;
using System.Text; using System.Text;
using ImGuiNET; using ImGuiNET;
namespace Pal.Client.Extensions namespace Pal.Client.Extensions;
internal static class PalImGui
{ {
internal static class PalImGui
{
/// <summary> /// <summary>
/// None of the default BeginTabItem methods allow using flags without making the tab have a close button for some reason. /// None of the default BeginTabItem methods allow using flags without making the tab have a close button for some reason.
/// </summary> /// </summary>
@ -32,5 +32,4 @@ namespace Pal.Client.Extensions
if (ImGui.IsItemClicked()) if (ImGui.IsItemClicked())
choice = value; choice = value;
} }
}
} }

View File

@ -1,12 +1,12 @@
using System; using System;
namespace Pal.Client.Floors namespace Pal.Client.Floors;
/// <summary>
/// This is a currently-visible marker.
/// </summary>
internal sealed class EphemeralLocation : MemoryLocation
{ {
/// <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 bool Equals(object? obj) => obj is EphemeralLocation && base.Equals(obj);
public override int GetHashCode() => base.GetHashCode(); public override int GetHashCode() => base.GetHashCode();
@ -25,5 +25,4 @@ namespace Pal.Client.Floors
{ {
return $"EphemeralLocation(Position={Position}, Type={Type})"; return $"EphemeralLocation(Position={Position}, Type={Type})";
} }
}
} }

View File

@ -10,10 +10,10 @@ using Pal.Client.Floors.Tasks;
using Pal.Client.Net; using Pal.Client.Net;
using Pal.Common; using Pal.Common;
namespace Pal.Client.Floors namespace Pal.Client.Floors;
internal sealed class FloorService
{ {
internal sealed class FloorService
{
private readonly IPalacePalConfiguration _configuration; private readonly IPalacePalConfiguration _configuration;
private readonly Cleanup _cleanup; private readonly Cleanup _cleanup;
private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IServiceScopeFactory _serviceScopeFactory;
@ -159,5 +159,4 @@ namespace Pal.Client.Floors
memoryTerritory.ReadyState = MemoryTerritory.EReadyState.Importing; memoryTerritory.ReadyState = MemoryTerritory.EReadyState.Importing;
} }
} }
}
} }

View File

@ -4,10 +4,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Pal.Client.Configuration; using Pal.Client.Configuration;
@ -18,21 +16,21 @@ using Pal.Client.Rendering;
using Pal.Client.Scheduled; using Pal.Client.Scheduled;
using Pal.Common; using Pal.Common;
namespace Pal.Client.Floors namespace Pal.Client.Floors;
internal sealed class FrameworkService : IDisposable
{ {
internal sealed class FrameworkService : IDisposable
{
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly ILogger<FrameworkService> _logger; private readonly ILogger<FrameworkService> _logger;
private readonly Framework _framework; private readonly IFramework _framework;
private readonly ConfigurationManager _configurationManager; private readonly ConfigurationManager _configurationManager;
private readonly IPalacePalConfiguration _configuration; private readonly IPalacePalConfiguration _configuration;
private readonly ClientState _clientState; private readonly IClientState _clientState;
private readonly TerritoryState _territoryState; private readonly TerritoryState _territoryState;
private readonly FloorService _floorService; private readonly FloorService _floorService;
private readonly DebugState _debugState; private readonly DebugState _debugState;
private readonly RenderAdapter _renderAdapter; private readonly RenderAdapter _renderAdapter;
private readonly ObjectTable _objectTable; private readonly IObjectTable _objectTable;
private readonly RemoteApi _remoteApi; private readonly RemoteApi _remoteApi;
internal Queue<IQueueOnFrameworkThread> EarlyEventQueue { get; } = new(); internal Queue<IQueueOnFrameworkThread> EarlyEventQueue { get; } = new();
@ -42,15 +40,15 @@ namespace Pal.Client.Floors
public FrameworkService( public FrameworkService(
IServiceProvider serviceProvider, IServiceProvider serviceProvider,
ILogger<FrameworkService> logger, ILogger<FrameworkService> logger,
Framework framework, IFramework framework,
ConfigurationManager configurationManager, ConfigurationManager configurationManager,
IPalacePalConfiguration configuration, IPalacePalConfiguration configuration,
ClientState clientState, IClientState clientState,
TerritoryState territoryState, TerritoryState territoryState,
FloorService floorService, FloorService floorService,
DebugState debugState, DebugState debugState,
RenderAdapter renderAdapter, RenderAdapter renderAdapter,
ObjectTable objectTable, IObjectTable objectTable,
RemoteApi remoteApi) RemoteApi remoteApi)
{ {
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
@ -79,7 +77,7 @@ namespace Pal.Client.Floors
private void OnSaved(object? sender, IPalacePalConfiguration? config) private void OnSaved(object? sender, IPalacePalConfiguration? config)
=> EarlyEventQueue.Enqueue(new QueuedConfigUpdate()); => EarlyEventQueue.Enqueue(new QueuedConfigUpdate());
private void OnUpdate(Framework framework) private void OnUpdate(IFramework framework)
{ {
if (_configuration.FirstUse) if (_configuration.FirstUse)
return; return;
@ -110,6 +108,12 @@ namespace Pal.Client.Floors
if (!_territoryState.IsInDeepDungeon() || !_floorService.IsReady(_territoryState.LastTerritory)) if (!_territoryState.IsInDeepDungeon() || !_floorService.IsReady(_territoryState.LastTerritory))
return; return;
if (_renderAdapter.RequireRedraw)
{
recreateLayout = true;
_renderAdapter.RequireRedraw = false;
}
ETerritoryType territoryType = (ETerritoryType)_territoryState.LastTerritory; ETerritoryType territoryType = (ETerritoryType)_territoryState.LastTerritory;
MemoryTerritory memoryTerritory = _floorService.GetTerritoryIfReady(territoryType)!; MemoryTerritory memoryTerritory = _floorService.GetTerritoryIfReady(territoryType)!;
if (_configuration.Mode == EMode.Online && memoryTerritory.SyncState == ESyncState.NotAttempted) if (_configuration.Mode == EMode.Online && memoryTerritory.SyncState == ESyncState.NotAttempted)
@ -171,12 +175,20 @@ namespace Pal.Client.Floors
{ {
foreach (var location in memoryTerritory.Locations) foreach (var location in memoryTerritory.Locations)
{ {
uint desiredColor = DetermineColor(location, visibleLocations); bool isEnabled = DetermineVisibility(location, visibleLocations);
if (location.RenderElement == null || !location.RenderElement.IsValid) if (location.RenderElement == null)
{
if (isEnabled)
return true;
else
continue;
}
if (!location.RenderElement.IsValid)
return true; return true;
if (location.RenderElement.Color != desiredColor) if (location.RenderElement.Enabled != isEnabled)
location.RenderElement.Color = desiredColor; location.RenderElement.Enabled = isEnabled;
} }
} }
catch (Exception e) catch (Exception e)
@ -221,12 +233,12 @@ namespace Pal.Client.Floors
{ {
if (location.Type == MemoryLocation.EType.Trap) if (location.Type == MemoryLocation.EType.Trap)
{ {
CreateRenderElement(location, elements, DetermineColor(location, visibleMarkers), CreateRenderElement(location, elements, DetermineVisibility(location, visibleMarkers),
_configuration.DeepDungeons.Traps); _configuration.DeepDungeons.Traps);
} }
else if (location.Type == MemoryLocation.EType.Hoard) else if (location.Type == MemoryLocation.EType.Hoard)
{ {
CreateRenderElement(location, elements, DetermineColor(location, visibleMarkers), CreateRenderElement(location, elements, DetermineVisibility(location, visibleMarkers),
_configuration.DeepDungeons.HoardCoffers); _configuration.DeepDungeons.HoardCoffers);
} }
} }
@ -247,8 +259,12 @@ namespace Pal.Client.Floors
if (location.Type == MemoryLocation.EType.SilverCoffer && if (location.Type == MemoryLocation.EType.SilverCoffer &&
_configuration.DeepDungeons.SilverCoffers.Show) _configuration.DeepDungeons.SilverCoffers.Show)
{ {
CreateRenderElement(location, elements, DetermineColor(location), CreateRenderElement(location, elements, true, _configuration.DeepDungeons.SilverCoffers);
_configuration.DeepDungeons.SilverCoffers); }
else if (location.Type == MemoryLocation.EType.GoldCoffer &&
_configuration.DeepDungeons.GoldCoffers.Show)
{
CreateRenderElement(location, elements, true, _configuration.DeepDungeons.GoldCoffers);
} }
} }
@ -258,7 +274,7 @@ namespace Pal.Client.Floors
_renderAdapter.SetLayer(ELayer.RegularCoffers, elements); _renderAdapter.SetLayer(ELayer.RegularCoffers, elements);
} }
private uint DetermineColor(PersistentLocation location, IReadOnlyList<PersistentLocation> visibleLocations) private bool DetermineVisibility(PersistentLocation location, IReadOnlyList<PersistentLocation> visibleLocations)
{ {
switch (location.Type) switch (location.Type)
{ {
@ -266,32 +282,28 @@ namespace Pal.Client.Floors
when _territoryState.PomanderOfSight == PomanderState.Inactive || when _territoryState.PomanderOfSight == PomanderState.Inactive ||
!_configuration.DeepDungeons.Traps.OnlyVisibleAfterPomander || !_configuration.DeepDungeons.Traps.OnlyVisibleAfterPomander ||
visibleLocations.Any(x => x == location): visibleLocations.Any(x => x == location):
return _configuration.DeepDungeons.Traps.Color; return true;
case MemoryLocation.EType.Hoard case MemoryLocation.EType.Hoard
when _territoryState.PomanderOfIntuition == PomanderState.Inactive || when _territoryState.PomanderOfIntuition == PomanderState.Inactive ||
!_configuration.DeepDungeons.HoardCoffers.OnlyVisibleAfterPomander || !_configuration.DeepDungeons.HoardCoffers.OnlyVisibleAfterPomander ||
visibleLocations.Any(x => x == location): visibleLocations.Any(x => x == location):
return _configuration.DeepDungeons.HoardCoffers.Color; return true;
default: default:
return RenderData.ColorInvisible; return false;
} }
} }
private uint DetermineColor(EphemeralLocation location) private void CreateRenderElement(MemoryLocation location, List<IRenderElement> elements, bool enabled,
{
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)
{
location.RenderElement = null;
return; return;
}
var element = _renderAdapter.CreateElement(location.Type, location.Position, color, config.Fill); var element =
_renderAdapter.CreateElement(location.Type, location.Position, enabled, config.Color, config.Fill);
location.RenderElement = element; location.RenderElement = element;
elements.Add(element); elements.Add(element);
} }
@ -371,7 +383,7 @@ namespace Pal.Client.Floors
List<EphemeralLocation> ephemeralLocations = 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]; IGameObject? obj = _objectTable[i];
if (obj == null) if (obj == null)
continue; continue;
@ -383,6 +395,7 @@ namespace Pal.Client.Floors
case 2007185: case 2007185:
case 2007186: case 2007186:
case 2009504: case 2009504:
case 2013284:
persistentLocations.Add(new PersistentLocation persistentLocations.Add(new PersistentLocation
{ {
Type = MemoryLocation.EType.Trap, Type = MemoryLocation.EType.Trap,
@ -411,6 +424,15 @@ namespace Pal.Client.Floors
Seen = true, Seen = true,
}); });
break; break;
case 2007358:
ephemeralLocations.Add(new EphemeralLocation
{
Type = MemoryLocation.EType.GoldCoffer,
Position = obj.Position,
Seen = true
});
break;
} }
} }
@ -425,7 +447,6 @@ namespace Pal.Client.Floors
Position = obj.Position, Position = obj.Position,
Seen = true, Seen = true,
Source = ClientLocation.ESource.ExplodedLocally, Source = ClientLocation.ESource.ExplodedLocally,
}); });
} }
} }
@ -440,5 +461,4 @@ namespace Pal.Client.Floors
handler.RunIfCompatible(queued, ref recreateLayout); handler.RunIfCompatible(queued, ref recreateLayout);
} }
}
} }

View File

@ -5,13 +5,13 @@ using Pal.Client.Rendering;
using Pal.Common; using Pal.Common;
using Palace; using Palace;
namespace Pal.Client.Floors namespace Pal.Client.Floors;
/// <summary>
/// Base class for <see cref="MemoryLocation"/> and <see cref="EphemeralLocation"/>.
/// </summary>
internal abstract class MemoryLocation
{ {
/// <summary>
/// Base class for <see cref="MemoryLocation"/> and <see cref="EphemeralLocation"/>.
/// </summary>
internal abstract class MemoryLocation
{
public required EType Type { get; init; } public required EType Type { get; init; }
public required Vector3 Position { get; init; } public required Vector3 Position { get; init; }
public bool Seen { get; set; } public bool Seen { get; set; }
@ -26,6 +26,7 @@ namespace Pal.Client.Floors
Hoard, Hoard,
SilverCoffer, SilverCoffer,
GoldCoffer,
} }
public override bool Equals(object? obj) public override bool Equals(object? obj)
@ -39,10 +40,10 @@ namespace Pal.Client.Floors
{ {
return HashCode.Combine(Type, PalaceMath.GetHashCode(Position)); return HashCode.Combine(Type, PalaceMath.GetHashCode(Position));
} }
} }
internal static class ETypeExtensions internal static class ETypeExtensions
{ {
public static MemoryLocation.EType ToMemoryType(this ObjectType objectType) public static MemoryLocation.EType ToMemoryType(this ObjectType objectType)
{ {
return objectType switch return objectType switch
@ -62,5 +63,4 @@ namespace Pal.Client.Floors
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null) _ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
}; };
} }
}
} }

View File

@ -5,13 +5,13 @@ using Pal.Client.Configuration;
using Pal.Client.Scheduled; using Pal.Client.Scheduled;
using Pal.Common; using Pal.Common;
namespace Pal.Client.Floors 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
{ {
/// <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) public MemoryTerritory(ETerritoryType territoryType)
{ {
TerritoryType = territoryType; TerritoryType = territoryType;
@ -59,5 +59,4 @@ namespace Pal.Client.Floors
/// </summary> /// </summary>
Importing, Importing,
} }
}
} }

View File

@ -0,0 +1,99 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using ImGuiNET;
namespace Pal.Client.Floors;
/// <summary>
/// This isn't very useful for running deep dungeons normally, but it is for plugin dev.
///
/// Needs the corresponding beta feature to be enabled.
/// </summary>
internal sealed class ObjectTableDebug : IDisposable
{
public const string FeatureName = nameof(ObjectTableDebug);
private readonly IDalamudPluginInterface _pluginInterface;
private readonly IObjectTable _objectTable;
private readonly IGameGui _gameGui;
private readonly IClientState _clientState;
public ObjectTableDebug(IDalamudPluginInterface pluginInterface, IObjectTable objectTable, IGameGui gameGui,
IClientState clientState)
{
_pluginInterface = pluginInterface;
_objectTable = objectTable;
_gameGui = gameGui;
_clientState = clientState;
_pluginInterface.UiBuilder.Draw += Draw;
}
private void Draw()
{
int index = 0;
foreach (IGameObject obj in _objectTable)
{
if (obj is IEventObj eventObj && string.IsNullOrEmpty(eventObj.Name.ToString()))
{
++index;
int model = Marshal.ReadInt32(obj.Address + 128);
if (_gameGui.WorldToScreen(obj.Position, out var screenCoords))
{
// So, while WorldToScreen will return false if the point is off of game client screen, to
// to avoid performance issues, we have to manually determine if creating a window would
// produce a new viewport, and skip rendering it if so
float distance = DistanceToPlayer(obj.Position);
var objectText =
$"{obj.Address.ToInt64():X}:{obj.EntityId:X}[{index}]\nkind: {obj.ObjectKind} sub: {obj.SubKind}\nmodel: {model}\nname: {obj.Name}\ndata id: {obj.DataId}";
var screenPos = ImGui.GetMainViewport().Pos;
var screenSize = ImGui.GetMainViewport().Size;
var windowSize = ImGui.CalcTextSize(objectText);
// Add some extra safety padding
windowSize.X += ImGui.GetStyle().WindowPadding.X + 10;
windowSize.Y += ImGui.GetStyle().WindowPadding.Y + 10;
if (screenCoords.X + windowSize.X > screenPos.X + screenSize.X ||
screenCoords.Y + windowSize.Y > screenPos.Y + screenSize.Y)
continue;
if (distance > 50f)
continue;
ImGui.SetNextWindowPos(new Vector2(screenCoords.X, screenCoords.Y));
ImGui.SetNextWindowBgAlpha(Math.Max(1f - (distance / 50f), 0.2f));
if (ImGui.Begin(
$"PalacePal_{nameof(ObjectTableDebug)}_{index}",
ImGuiWindowFlags.NoDecoration |
ImGuiWindowFlags.AlwaysAutoResize |
ImGuiWindowFlags.NoSavedSettings |
ImGuiWindowFlags.NoMove |
ImGuiWindowFlags.NoMouseInputs |
ImGuiWindowFlags.NoDocking |
ImGuiWindowFlags.NoFocusOnAppearing |
ImGuiWindowFlags.NoNav))
ImGui.Text(objectText);
ImGui.End();
}
}
}
}
private float DistanceToPlayer(Vector3 center)
=> Vector3.Distance(_clientState.LocalPlayer?.Position ?? Vector3.Zero, center);
public void Dispose()
{
_pluginInterface.UiBuilder.Draw -= Draw;
}
}

View File

@ -2,13 +2,13 @@
using System.Collections.Generic; using System.Collections.Generic;
using Pal.Client.Database; using Pal.Client.Database;
namespace Pal.Client.Floors namespace Pal.Client.Floors;
/// <summary>
/// A <see cref="ClientLocation"/> loaded in memory, with certain extra attributes as needed.
/// </summary>
internal sealed class PersistentLocation : MemoryLocation
{ {
/// <summary>
/// A <see cref="ClientLocation"/> loaded in memory, with certain extra attributes as needed.
/// </summary>
internal sealed class PersistentLocation : MemoryLocation
{
/// <see cref="ClientLocation.LocalId"/> /// <see cref="ClientLocation.LocalId"/>
public int? LocalId { get; set; } public int? LocalId { get; set; }
@ -51,5 +51,4 @@ namespace Pal.Client.Floors
{ {
return $"PersistentLocation(Position={Position}, Type={Type})"; return $"PersistentLocation(Position={Position}, Type={Type})";
} }
}
} }

View File

@ -4,11 +4,11 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Pal.Client.Database; using Pal.Client.Database;
namespace Pal.Client.Floors.Tasks namespace Pal.Client.Floors.Tasks;
{
internal abstract class DbTask<T> internal abstract class DbTask<T>
where T : DbTask<T> where T : DbTask<T>
{ {
private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IServiceScopeFactory _serviceScopeFactory;
protected DbTask(IServiceScopeFactory serviceScopeFactory) protected DbTask(IServiceScopeFactory serviceScopeFactory)
@ -24,18 +24,23 @@ namespace Pal.Client.Floors.Tasks
{ {
using var scope = _serviceScopeFactory.CreateScope(); using var scope = _serviceScopeFactory.CreateScope();
ILogger<T> logger = scope.ServiceProvider.GetRequiredService<ILogger<T>>(); ILogger<T> logger = scope.ServiceProvider.GetRequiredService<ILogger<T>>();
try
{
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>(); using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
Run(dbContext, logger); Run(dbContext, logger);
} }
catch (Exception e) catch (Exception e)
{ {
DependencyInjectionContext.LoggerProvider.CreateLogger<DbTask<T>>() logger.LogError(e, "Failed to run DbTask");
.LogError(e, "Failed to run DbTask"); }
}
catch (Exception)
{
// nothing we can do here but catch it, if we don't we crash the game
} }
}); });
} }
protected abstract void Run(PalClientContext dbContext, ILogger<T> logger); protected abstract void Run(PalClientContext dbContext, ILogger<T> logger);
}
} }

View File

@ -7,10 +7,10 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Pal.Client.Database; using Pal.Client.Database;
namespace Pal.Client.Floors.Tasks namespace Pal.Client.Floors.Tasks;
internal sealed class LoadTerritory : DbTask<LoadTerritory>
{ {
internal sealed class LoadTerritory : DbTask<LoadTerritory>
{
private readonly Cleanup _cleanup; private readonly Cleanup _cleanup;
private readonly MemoryTerritory _territory; private readonly MemoryTerritory _territory;
@ -75,5 +75,4 @@ namespace Pal.Client.Floors.Tasks
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null) _ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
}; };
} }
}
} }

View File

@ -5,10 +5,10 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Pal.Client.Database; using Pal.Client.Database;
namespace Pal.Client.Floors.Tasks namespace Pal.Client.Floors.Tasks;
internal sealed class MarkLocalSeen : DbTask<MarkLocalSeen>
{ {
internal sealed class MarkLocalSeen : DbTask<MarkLocalSeen>
{
private readonly MemoryTerritory _territory; private readonly MemoryTerritory _territory;
private readonly IReadOnlyList<PersistentLocation> _locations; private readonly IReadOnlyList<PersistentLocation> _locations;
@ -24,13 +24,14 @@ namespace Pal.Client.Floors.Tasks
{ {
lock (_territory.LockObj) lock (_territory.LockObj)
{ {
logger.LogInformation("Marking {Count} locations as seen locally in territory {Territory}", _locations.Count, logger.LogInformation("Marking {Count} locations as seen locally in territory {Territory}",
_locations.Count,
_territory.TerritoryType); _territory.TerritoryType);
List<int> localIds = _locations.Select(l => l.LocalId).Where(x => x != null).Cast<int>().ToList();
dbContext.Locations dbContext.Locations
.Where(loc => _locations.Any(l => l.LocalId == loc.LocalId)) .Where(loc => localIds.Contains(loc.LocalId))
.ExecuteUpdate(loc => loc.SetProperty(l => l.Seen, true)); .ExecuteUpdate(loc => loc.SetProperty(l => l.Seen, true));
dbContext.SaveChanges(); dbContext.SaveChanges();
} }
} }
}
} }

View File

@ -5,10 +5,10 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Pal.Client.Database; using Pal.Client.Database;
namespace Pal.Client.Floors.Tasks namespace Pal.Client.Floors.Tasks;
internal sealed class MarkRemoteSeen : DbTask<MarkRemoteSeen>
{ {
internal sealed class MarkRemoteSeen : DbTask<MarkRemoteSeen>
{
private readonly MemoryTerritory _territory; private readonly MemoryTerritory _territory;
private readonly IReadOnlyList<PersistentLocation> _locations; private readonly IReadOnlyList<PersistentLocation> _locations;
private readonly string _accountId; private readonly string _accountId;
@ -47,5 +47,4 @@ namespace Pal.Client.Floors.Tasks
dbContext.SaveChanges(); dbContext.SaveChanges();
} }
} }
}
} }

View File

@ -6,10 +6,10 @@ using Microsoft.Extensions.Logging;
using Pal.Client.Database; using Pal.Client.Database;
using Pal.Common; using Pal.Common;
namespace Pal.Client.Floors.Tasks namespace Pal.Client.Floors.Tasks;
internal sealed class SaveNewLocations : DbTask<SaveNewLocations>
{ {
internal sealed class SaveNewLocations : DbTask<SaveNewLocations>
{
private readonly MemoryTerritory _territory; private readonly MemoryTerritory _territory;
private readonly List<PersistentLocation> _newLocations; private readonly List<PersistentLocation> _newLocations;
@ -73,5 +73,4 @@ namespace Pal.Client.Floors.Tasks
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null) _ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
}; };
} }
}
} }

View File

@ -1,15 +1,15 @@
using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Conditions; using Dalamud.Plugin.Services;
using Pal.Common; using Pal.Common;
namespace Pal.Client.Floors namespace Pal.Client.Floors;
{
public sealed class TerritoryState
{
private readonly ClientState _clientState;
private readonly Condition _condition;
public TerritoryState(ClientState clientState, Condition condition) public sealed class TerritoryState
{
private readonly IClientState _clientState;
private readonly ICondition _condition;
public TerritoryState(IClientState clientState, ICondition condition)
{ {
_clientState = clientState; _clientState = clientState;
_condition = condition; _condition = condition;
@ -23,14 +23,12 @@ namespace Pal.Client.Floors
_clientState.IsLoggedIn _clientState.IsLoggedIn
&& _condition[ConditionFlag.InDeepDungeon] && _condition[ConditionFlag.InDeepDungeon]
&& typeof(ETerritoryType).IsEnumDefined(_clientState.TerritoryType); && typeof(ETerritoryType).IsEnumDefined(_clientState.TerritoryType);
}
} public enum PomanderState
{
public enum PomanderState
{
Inactive, Inactive,
Active, Active,
FoundOnCurrentFloor, FoundOnCurrentFloor,
PomanderOfSafetyUsed, PomanderOfSafetyUsed,
}
} }

View File

@ -4,10 +4,9 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Pal.Client namespace Pal.Client;
internal interface ILanguageChanged
{ {
internal interface ILanguageChanged
{
void LanguageChanged(); void LanguageChanged();
}
} }

View File

@ -1,15 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using System.Text.Json.Serialization;
using System.Text.Json; using System.Text.Json;
using System.Threading.Tasks; using System.Text.Json.Serialization;
namespace Pal.Client.Net namespace Pal.Client.Net;
internal sealed class JwtClaims
{ {
internal sealed class JwtClaims
{
[JsonPropertyName("nameid")] [JsonPropertyName("nameid")]
public Guid NameId { get; set; } public Guid NameId { get; set; }
@ -42,12 +40,13 @@ namespace Pal.Client.Net
payload += "="; payload += "=";
string content = Encoding.UTF8.GetString(Convert.FromBase64String(payload)); string content = Encoding.UTF8.GetString(Convert.FromBase64String(payload));
return JsonSerializer.Deserialize<JwtClaims>(content) ?? throw new InvalidOperationException("token deserialization returned null"); return JsonSerializer.Deserialize<JwtClaims>(content) ??
} throw new InvalidOperationException("token deserialization returned null");
} }
}
internal sealed class JwtRoleConverter : JsonConverter<List<string>> internal sealed class JwtRoleConverter : JsonConverter<List<string>>
{ {
public override List<string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override List<string> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
if (reader.TokenType == JsonTokenType.String) if (reader.TokenType == JsonTokenType.String)
@ -75,21 +74,6 @@ namespace Pal.Client.Net
throw new JsonException("bad token type"); throw new JsonException("bad token type");
} }
public override void Write(Utf8JsonWriter writer, List<string> value, JsonSerializerOptions options) => throw new NotImplementedException(); public override void Write(Utf8JsonWriter writer, List<string> value, JsonSerializerOptions options) =>
} throw new NotImplementedException();
public sealed class JwtDateConverter : JsonConverter<DateTimeOffset>
{
static readonly DateTimeOffset Zero = new(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.Number)
throw new JsonException("bad token type");
return Zero.AddSeconds(reader.GetInt64());
}
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) => throw new NotImplementedException();
}
} }

View File

@ -0,0 +1,21 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Pal.Client.Net;
public sealed class JwtDateConverter : JsonConverter<DateTimeOffset>
{
static readonly DateTimeOffset Zero = new(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.Number)
throw new JsonException("bad token type");
return Zero.AddSeconds(reader.GetInt64());
}
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) =>
throw new NotImplementedException();
}

View File

@ -1,21 +1,23 @@
using Account; using System;
using Grpc.Core;
using Grpc.Net.Client;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Account;
using Grpc.Core;
using Grpc.Net.Client;
using Microsoft.Extensions.Logging;
using Pal.Client.Configuration;
using Pal.Client.Extensions; using Pal.Client.Extensions;
using Pal.Client.Properties; using Pal.Client.Properties;
using Pal.Client.Configuration; using Version = System.Version;
namespace Pal.Client.Net namespace Pal.Client.Net;
internal partial class RemoteApi
{ {
internal partial class RemoteApi private static readonly Version PluginVersion = typeof(Plugin).Assembly.GetName().Version!;
{
private readonly SemaphoreSlim _connectLock = new(1, 1); private readonly SemaphoreSlim _connectLock = new(1, 1);
private async Task<(bool Success, string Error)> TryConnect(CancellationToken cancellationToken, private async Task<(bool Success, string Error)> TryConnect(CancellationToken cancellationToken,
@ -73,7 +75,14 @@ namespace Pal.Client.Net
if (configuredAccount == null) if (configuredAccount == null)
{ {
_logger.LogInformation("No account information saved for {Url}, creating new account", RemoteUrl); _logger.LogInformation("No account information saved for {Url}, creating new account", RemoteUrl);
var createAccountReply = await accountClient.CreateAccountAsync(new CreateAccountRequest(), var createAccountReply = await accountClient.CreateAccountAsync(new CreateAccountRequest
{
Version = new()
{
Major = PluginVersion.Major,
Minor = PluginVersion.Minor,
},
},
headers: UnauthorizedHeaders(), deadline: DateTime.UtcNow.AddSeconds(10), headers: UnauthorizedHeaders(), deadline: DateTime.UtcNow.AddSeconds(10),
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
if (createAccountReply.Success) if (createAccountReply.Success)
@ -115,7 +124,15 @@ namespace Pal.Client.Net
_logger.LogInformation("Logging in with account id {AccountId}", _logger.LogInformation("Logging in with account id {AccountId}",
configuredAccount.AccountId.ToPartialId()); configuredAccount.AccountId.ToPartialId());
LoginReply loginReply = await accountClient.LoginAsync( LoginReply loginReply = await accountClient.LoginAsync(
new LoginRequest { AccountId = configuredAccount.AccountId.ToString() }, new LoginRequest
{
AccountId = configuredAccount.AccountId.ToString(),
Version = new()
{
Major = PluginVersion.Major,
Minor = PluginVersion.Minor,
},
},
headers: UnauthorizedHeaders(), deadline: DateTime.UtcNow.AddSeconds(10), headers: UnauthorizedHeaders(), deadline: DateTime.UtcNow.AddSeconds(10),
cancellationToken: cancellationToken); cancellationToken: cancellationToken);
@ -228,5 +245,4 @@ namespace Pal.Client.Net
public bool IsValid => IsLoggedIn && !IsExpired; public bool IsValid => IsLoggedIn && !IsExpired;
} }
}
} }

View File

@ -1,12 +1,12 @@
using Account; using System;
using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Export;
namespace Pal.Client.Net namespace Pal.Client.Net;
internal partial class RemoteApi
{ {
internal partial class RemoteApi
{
public async Task<(bool, ExportRoot)> DoExport(CancellationToken cancellationToken = default) public async Task<(bool, ExportRoot)> DoExport(CancellationToken cancellationToken = default)
{ {
if (!await Connect(cancellationToken)) if (!await Connect(cancellationToken))
@ -16,8 +16,8 @@ namespace Pal.Client.Net
var exportReply = await exportClient.ExportAsync(new ExportRequest var exportReply = await exportClient.ExportAsync(new ExportRequest
{ {
ServerUrl = RemoteUrl, ServerUrl = RemoteUrl,
}, headers: AuthorizedHeaders(), deadline: DateTime.UtcNow.AddSeconds(120), cancellationToken: cancellationToken); }, headers: AuthorizedHeaders(), deadline: DateTime.UtcNow.AddSeconds(120),
cancellationToken: cancellationToken);
return (exportReply.Success, exportReply.Data); return (exportReply.Success, exportReply.Data);
} }
}
} }

View File

@ -1,5 +1,4 @@
using Palace; using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
@ -7,22 +6,27 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Pal.Client.Database; using Pal.Client.Database;
using Pal.Client.Floors; using Pal.Client.Floors;
using Palace;
namespace Pal.Client.Net namespace Pal.Client.Net;
internal partial class RemoteApi
{ {
internal partial class RemoteApi public async Task<(bool, List<PersistentLocation>)> 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(CreateLocationFromNetworkObject).ToList()); return (downloadReply.Success, downloadReply.Objects.Select(CreateLocationFromNetworkObject).ToList());
} }
public async Task<(bool, List<PersistentLocation>)> UploadLocations(ushort territoryType, IReadOnlyList<PersistentLocation> locations, CancellationToken cancellationToken = default) public async Task<(bool, List<PersistentLocation>)> UploadLocations(ushort territoryType,
IReadOnlyList<PersistentLocation> locations, CancellationToken cancellationToken = default)
{ {
if (locations.Count == 0) if (locations.Count == 0)
return (true, new()); return (true, new());
@ -42,11 +46,13 @@ namespace Pal.Client.Net
Y = m.Position.Y, Y = m.Position.Y,
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(CreateLocationFromNetworkObject).ToList()); return (uploadReply.Success, uploadReply.Objects.Select(CreateLocationFromNetworkObject).ToList());
} }
public async Task<bool> MarkAsSeen(ushort territoryType, IReadOnlyList<PersistentLocation> locations, CancellationToken cancellationToken = default) public async Task<bool> MarkAsSeen(ushort territoryType, IReadOnlyList<PersistentLocation> locations,
CancellationToken cancellationToken = default)
{ {
if (locations.Count == 0) if (locations.Count == 0)
return true; return true;
@ -59,7 +65,8 @@ namespace Pal.Client.Net
foreach (var marker in locations) 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;
} }
@ -80,8 +87,9 @@ namespace Pal.Client.Net
return new(false, new List<FloorStatistics>()); return new(false, new List<FloorStatistics>());
var palaceClient = new PalaceService.PalaceServiceClient(_channel); var palaceClient = new PalaceService.PalaceServiceClient(_channel);
var statisticsReply = await palaceClient.FetchStatisticsAsync(new StatisticsRequest(), headers: AuthorizedHeaders(), deadline: DateTime.UtcNow.AddSeconds(30), cancellationToken: cancellationToken); var statisticsReply = await palaceClient.FetchStatisticsAsync(new StatisticsRequest(),
headers: AuthorizedHeaders(), deadline: DateTime.UtcNow.AddSeconds(30),
cancellationToken: cancellationToken);
return (statisticsReply.Success, statisticsReply.FloorStatistics.ToList()); return (statisticsReply.Success, statisticsReply.FloorStatistics.ToList());
} }
}
} }

View File

@ -1,14 +1,14 @@
using System; using System;
using Dalamud.Logging;
using Grpc.Core;
using System.Net.Security; using System.Net.Security;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using Dalamud.Logging;
using Grpc.Core;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Pal.Client.Net namespace Pal.Client.Net;
internal partial class RemoteApi
{ {
internal partial class RemoteApi
{
private Metadata UnauthorizedHeaders() => new() private Metadata UnauthorizedHeaders() => new()
{ {
{ "User-Agent", _userAgent }, { "User-Agent", _userAgent },
@ -54,5 +54,4 @@ namespace Pal.Client.Net
return null; return null;
#endif #endif
} }
}
} }

View File

@ -1,19 +1,19 @@
using Dalamud.Logging; using System;
using Dalamud.Game.Gui;
using Dalamud.Logging;
using Grpc.Net.Client; using Grpc.Net.Client;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System;
using Dalamud.Game.Gui;
using Pal.Client.Configuration; using Pal.Client.Configuration;
using Pal.Client.DependencyInjection; using Pal.Client.DependencyInjection;
namespace Pal.Client.Net namespace Pal.Client.Net;
internal sealed partial class RemoteApi : IDisposable
{ {
internal sealed partial class RemoteApi : IDisposable
{
#if DEBUG #if DEBUG
public const string RemoteUrl = "http://localhost:5415"; public const string RemoteUrl = "http://localhost:5415";
#else #else
public const string RemoteUrl = "https://pal.liza.sh"; public const string RemoteUrl = "https://connect.palacepal.com";
#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)}";
@ -48,5 +48,4 @@ namespace Pal.Client.Net
_channel?.Dispose(); _channel?.Dispose();
_channel = null; _channel = null;
} }
}
} }

View File

@ -1,105 +1,54 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Dalamud.NET.Sdk/9.0.2">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework> <Version>6.0</Version>
<LangVersion>11.0</LangVersion>
<Nullable>enable</Nullable>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<PlatformTarget>x64</PlatformTarget>
<AssemblyName>Palace Pal</AssemblyName> <AssemblyName>Palace Pal</AssemblyName>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <PlatformTarget>x64</PlatformTarget>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugType>portable</DebugType>
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
<GitVersion>false</GitVersion>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
</PropertyGroup> </PropertyGroup>
<Import Project="..\vendor\LLib\LLib.targets"/>
<Import Project="..\vendor\LLib\RenameZip.targets"/>
<PropertyGroup Condition="'$(Configuration)' == 'Release'"> <PropertyGroup Condition="'$(Configuration)' == 'Release'">
<OutputPath>dist</OutputPath> <OutputPath>dist</OutputPath>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release' And Exists('Certificate.pfx')"> <ItemGroup Condition="'$(Configuration)' == 'Release' And Exists('Certificate.pfx')">
<None Remove="Certificate.pfx" /> <None Remove="Certificate.pfx"/>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release' And Exists('Certificate.pfx')"> <ItemGroup Condition="'$(Configuration)' == 'Release' And Exists('Certificate.pfx')">
<EmbeddedResource Include="Certificate.pfx" /> <EmbeddedResource Include="Certificate.pfx"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DalamudPackager" Version="2.1.10" /> <PackageReference Include="Dalamud.Extensions.MicrosoftLogging" Version="4.0.1"/>
<PackageReference Include="Google.Protobuf" Version="3.21.12" /> <PackageReference Include="Google.Protobuf" Version="3.27.2" />
<PackageReference Include="Grpc.Net.Client" Version="2.51.0" /> <PackageReference Include="Grpc.Net.Client" Version="2.63.0"/>
<PackageReference Include="GitInfo" Version="2.3.0"> <PackageReference Include="Grpc.Tools" Version="2.64.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Grpc.Tools" Version="2.51.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.3"> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.6" Condition="'$(Configuration)' == 'EF'">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0"/>
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="7.0.1" /> <PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Pal.Common\Pal.Common.csproj" /> <ProjectReference Include="..\Pal.Common\Pal.Common.csproj"/>
<ProjectReference Include="..\vendor\ECommons\ECommons\ECommons.csproj" /> <ProjectReference Include="..\vendor\ECommons\ECommons\ECommons.csproj"/>
<ProjectReference Include="..\vendor\LLib\LLib.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Protobuf Include="..\Pal.Common\Protos\account.proto" Link="Protos\account.proto" GrpcServices="Client" Access="Internal" /> <Protobuf Include="..\Pal.Common\Protos\account.proto" Link="Protos\account.proto" GrpcServices="Client" Access="Internal"/>
<Protobuf Include="..\Pal.Common\Protos\palace.proto" Link="Protos\palace.proto" GrpcServices="Client" Access="Internal" /> <Protobuf Include="..\Pal.Common\Protos\palace.proto" Link="Protos\palace.proto" GrpcServices="Client" Access="Internal"/>
<Protobuf Include="..\Pal.Common\Protos\export.proto" Link="Protos\export.proto" GrpcServices="Client" Access="Internal" /> <Protobuf Include="..\Pal.Common\Protos\export.proto" Link="Protos\export.proto" GrpcServices="Client" Access="Internal"/>
</ItemGroup>
<ItemGroup>
<!--You may need to adjust these paths yourself. These point to a Dalamud assembly in AppData.-->
<Reference Include="Dalamud">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll</HintPath>
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
</Reference>
<Reference Include="ImGui.NET">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\ImGui.NET.dll</HintPath>
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
</Reference>
<Reference Include="ImGuiScene">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\ImGuiScene.dll</HintPath>
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
</Reference>
<Reference Include="Lumina">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.dll</HintPath>
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
</Reference>
<Reference Include="Lumina.Excel">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll</HintPath>
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Newtonsoft.Json.dll</HintPath>
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
</Reference>
<Reference Include="FFXIVClientStructs">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\FFXIVClientStructs.dll</HintPath>
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
</Reference>
<Reference Include="Serilog">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Serilog.dll</HintPath>
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -114,18 +63,7 @@
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<Target Name="PopulateInfo" DependsOnTargets="GitVersion" BeforeTargets="GetAssemblyVersion;GenerateNuspec;GetPackageContents">
<PropertyGroup>
<Version>$(GitSemVerMajor).$(GitSemVerMinor)</Version>
<PackageVersion>$(Version)</PackageVersion>
</PropertyGroup>
</Target>
<Target Name="RenameLatestZip" AfterTargets="PackagePlugin" Condition="'$(Configuration)' == 'Release'">
<Exec Command="rename &quot;$(OutDir)$(AssemblyName)\latest.zip&quot; &quot;$(AssemblyName)-$(Version).zip&quot;" />
</Target>
<Target Name="Clean"> <Target Name="Clean">
<RemoveDir Directories="dist" /> <RemoveDir Directories="dist"/>
</Target> </Target>
</Project> </Project>

View File

@ -3,7 +3,12 @@
"Author": "Liza Carvelli", "Author": "Liza Carvelli",
"Punchline": "Shows possible trap & hoard coffer locations in Palace of the Dead & Heaven on High.", "Punchline": "Shows possible trap & hoard coffer locations in Palace of the Dead & Heaven on High.",
"Description": "Shows possible trap & hoard coffer locations in Palace of the Dead & Heaven on High.\n\nThe default configuration requires Splatoon to be installed. If you do not wish to install Splatoon, you can switch to the experimental 'Simple' renderer in the configuration.", "Description": "Shows possible trap & hoard coffer locations in Palace of the Dead & Heaven on High.\n\nThe default configuration requires Splatoon to be installed. If you do not wish to install Splatoon, you can switch to the experimental 'Simple' renderer in the configuration.",
"RepoUrl": "https://github.com/carvelli/PalacePal", "RepoUrl": "https://git.carvel.li/liza/PalacePal",
"IconUrl": "https://raw.githubusercontent.com/carvelli/Dalamud-Plugins/master/dist/Palace Pal.png", "IconUrl": "https://plugins.carvel.li/icons/PalacePal.png",
"Tags": [ "potd", "palace", "hoh", "splatoon" ] "Tags": [
"potd",
"palace",
"hoh",
"splatoon"
]
} }

View File

@ -1,61 +1,65 @@
using Dalamud.Interface.Windowing; using System;
using Dalamud.Plugin; using System.Collections.Generic;
using Pal.Client.Rendering;
using System;
using System.Globalization; using System.Globalization;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dalamud.Game; using Dalamud.Extensions.MicrosoftLogging;
using Dalamud.Game.ClientState;
using Dalamud.Game.Command; using Dalamud.Game.Command;
using Dalamud.Game.Gui; using Dalamud.Interface.Windowing;
using Pal.Client.Properties; using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using ECommons; using ECommons;
using ECommons.DalamudServices;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Pal.Client.Commands; using Pal.Client.Commands;
using Pal.Client.Configuration; using Pal.Client.Configuration;
using Pal.Client.DependencyInjection; using Pal.Client.DependencyInjection;
using Pal.Client.Properties;
using Pal.Client.Rendering;
namespace Pal.Client namespace Pal.Client;
/// <summary>
/// With all DI logic elsewhere, this plugin shell really only takes care of a few things around events that
/// need to be sent to different receivers depending on priority or configuration .
/// </summary>
/// <see cref="DependencyInjectionContext"/>
internal sealed class Plugin : IDalamudPlugin
{ {
/// <summary>
/// With all DI logic elsewhere, this plugin shell really only takes care of a few things around events that
/// need to be sent to different receivers depending on priority or configuration .
/// </summary>
/// <see cref="DependencyInjectionContext"/>
internal sealed class Plugin : IDalamudPlugin
{
private readonly CancellationTokenSource _initCts = new(); private readonly CancellationTokenSource _initCts = new();
private readonly DalamudPluginInterface _pluginInterface; private readonly IDalamudPluginInterface _pluginInterface;
private readonly CommandManager _commandManager; private readonly ICommandManager _commandManager;
private readonly ClientState _clientState; private readonly IClientState _clientState;
private readonly ChatGui _chatGui; private readonly IChatGui _chatGui;
private readonly Framework _framework; private readonly IFramework _framework;
private readonly TaskCompletionSource<IServiceScope> _rootScopeCompletionSource = new(); private readonly TaskCompletionSource<IServiceScope> _rootScopeCompletionSource = new();
private ELoadState _loadState = ELoadState.Initializing; private ELoadState _loadState = ELoadState.Initializing;
private DependencyInjectionContext? _dependencyInjectionContext; private DependencyInjectionContext? _dependencyInjectionContext;
private ILogger _logger = DependencyInjectionContext.LoggerProvider.CreateLogger<Plugin>(); private ILogger _logger;
private WindowSystem? _windowSystem; private WindowSystem? _windowSystem;
private IServiceScope? _rootScope; private IServiceScope? _rootScope;
private Action? _loginAction; private Action? _loginAction;
public Plugin( public Plugin(
DalamudPluginInterface pluginInterface, IDalamudPluginInterface pluginInterface,
CommandManager commandManager, ICommandManager commandManager,
ClientState clientState, IClientState clientState,
ChatGui chatGui, IChatGui chatGui,
Framework framework) IFramework framework,
IPluginLog pluginLog)
{ {
_pluginInterface = pluginInterface; _pluginInterface = pluginInterface;
_commandManager = commandManager; _commandManager = commandManager;
_clientState = clientState; _clientState = clientState;
_chatGui = chatGui; _chatGui = chatGui;
_framework = framework; _framework = framework;
_logger = new DalamudLoggerProvider(pluginLog).CreateLogger<Plugin>();
// set up the current UI language before creating anything // set up the current UI language before creating anything
Localization.Culture = new CultureInfo(_pluginInterface.UiLanguage); Localization.Culture = new CultureInfo(_pluginInterface.UiLanguage);
@ -65,11 +69,13 @@ namespace Pal.Client
HelpMessage = Localization.Command_pal_HelpText HelpMessage = Localization.Command_pal_HelpText
}); });
// Using TickScheduler requires ECommons to at least be partially initialized
// ECommonsMain.Dispose leaves this untouched.
Svc.Init(pluginInterface);
Task.Run(async () => await CreateDependencyContext()); Task.Run(async () => await CreateDependencyContext());
} }
public string Name => Localization.Palace_Pal;
private async Task CreateDependencyContext() private async Task CreateDependencyContext()
{ {
try try
@ -96,12 +102,10 @@ namespace Pal.Client
_rootScopeCompletionSource.SetResult(_rootScope); _rootScopeCompletionSource.SetResult(_rootScope);
_loadState = ELoadState.Loaded; _loadState = ELoadState.Loaded;
} }
catch (ObjectDisposedException e) catch (Exception e) when (e is ObjectDisposedException
{ or OperationCanceledException
_rootScopeCompletionSource.SetException(e); or RepoVerification.RepoVerificationFailedException
_loadState = ELoadState.Error; || (e is FileLoadException && _pluginInterface.IsDev))
}
catch (OperationCanceledException e)
{ {
_rootScopeCompletionSource.SetException(e); _rootScopeCompletionSource.SetException(e);
_loadState = ELoadState.Error; _loadState = ELoadState.Error;
@ -129,7 +133,7 @@ namespace Pal.Client
_loginAction = loginAction; _loginAction = loginAction;
} }
private void Login(object? sender, EventArgs eventArgs) private void Login()
{ {
_loginAction?.Invoke(); _loginAction?.Invoke();
_loginAction = null; _loginAction = null;
@ -165,39 +169,24 @@ namespace Pal.Client
return; return;
} }
var sp = rootScope.ServiceProvider; Action<string> commandHandler = rootScope.ServiceProvider
.GetRequiredService<IEnumerable<ISubCommand>>()
switch (arguments) .SelectMany(cmd => cmd.GetHandlers())
.Where(cmd => cmd.Key == arguments.ToLowerInvariant())
.Select(cmd => cmd.Value)
.SingleOrDefault(missingCommand =>
{ {
case "": chat.Error(string.Format(Localization.Command_pal_UnknownSubcommand, missingCommand,
case "config":
sp.GetRequiredService<PalConfigCommand>().Execute();
break;
case "stats":
sp.GetRequiredService<PalStatsCommand>().Execute();
break;
case "tc":
case "test-connection":
sp.GetRequiredService<PalTestConnectionCommand>().Execute();
break;
case "near":
case "tnear":
case "hnear":
sp.GetRequiredService<PalNearCommand>().Execute(arguments);
break;
default:
chat.Error(string.Format(Localization.Command_pal_UnknownSubcommand, arguments,
command)); command));
break; });
} commandHandler.Invoke(arguments);
} }
catch (Exception e) catch (Exception e)
{ {
chat.Error(e.ToString()); _logger.LogError(e, "Could not execute command '{Command}' with arguments '{Arguments}'", command,
arguments);
chat.Error(string.Format(Localization.Error_CommandFailed,
$"{e.GetType()} - {e.Message}"));
} }
}); });
} }
@ -243,5 +232,4 @@ namespace Pal.Client
Loaded, Loaded,
Error Error
} }
}
} }

View File

@ -1,8 +0,0 @@
using System.Reflection;
[assembly: AssemblyVersion(ThisAssembly.Git.SemVer.Major + "." + ThisAssembly.Git.SemVer.Minor)]
[assembly: AssemblyFileVersion(ThisAssembly.Git.SemVer.Major + "." + ThisAssembly.Git.SemVer.Minor)]
[assembly: AssemblyInformationalVersion(
ThisAssembly.Git.SemVer.Major + "." +
ThisAssembly.Git.SemVer.Minor + "+" +
ThisAssembly.Git.Commit)]

View File

@ -176,6 +176,43 @@ namespace Pal.Client.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Gold Coffer color.
/// </summary>
internal static string Config_GoldCoffer_Color {
get {
return ResourceManager.GetString("Config_GoldCoffer_Color", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Draw filled.
/// </summary>
internal static string Config_GoldCoffer_Filled {
get {
return ResourceManager.GetString("Config_GoldCoffer_Filled", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Show gold coffers on current floor.
/// </summary>
internal static string Config_GoldCoffer_Show {
get {
return ResourceManager.GetString("Config_GoldCoffer_Show", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Shows nearby gold coffers (containing pomanders) on the current floor.
///This is not synchronized with other players and not saved between floors/runs..
/// </summary>
internal static string Config_GoldCoffers_ToolTip {
get {
return ResourceManager.GetString("Config_GoldCoffers_ToolTip", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Hoard Coffer color. /// Looks up a localized string similar to Hoard Coffer color.
/// </summary> /// </summary>
@ -357,7 +394,7 @@ namespace Pal.Client.Properties {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Shows all the silver coffers visible to you on the current floor. /// Looks up a localized string similar to Shows nearby silver coffers (gear upgrades and magicites) on the current floor.
///This is not synchronized with other players and not saved between floors/runs.. ///This is not synchronized with other players and not saved between floors/runs..
/// </summary> /// </summary>
internal static string Config_SilverCoffers_ToolTip { internal static string Config_SilverCoffers_ToolTip {
@ -619,6 +656,15 @@ namespace Pal.Client.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Command could not be executed: {0}.
/// </summary>
internal static string Error_CommandFailed {
get {
return ResourceManager.GetString("Error_CommandFailed", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Please finish the initial setup first.. /// Looks up a localized string similar to Please finish the initial setup first..
/// </summary> /// </summary>

View File

@ -18,10 +18,39 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<!-- Common --> <!-- Common -->
<data name="PalaceOfTheDead" xml:space="preserve">
<value>Palast der Toten</value>
</data>
<data name="HeavenOnHigh" xml:space="preserve">
<value>Himmelssäule</value>
</data>
<data name="Save" xml:space="preserve">
<value>Speichern</value>
</data>
<data name="SaveAndClose" xml:space="preserve">
<value>Speichern &amp; Schließen</value>
</data>
<!-- Generic Errors --> <!-- Generic Errors -->
<!-- /pal commands --> <!-- /pal commands -->
<!-- Messages while connecting to the server. These are typically only visible when clicking 'Test connection'. --> <!-- Messages while connecting to the server. These are typically only visible when clicking 'Test connection'. -->
<data name="ConnectionSuccessful" xml:space="preserve">
<value>Verbindung erfolgreich.</value>
</data>
<data name="ConnectionError_NotOnline" xml:space="preserve">
<value>Sie sind nicht online.</value>
<comment>Shown if you attempt to connect to the server while you have selected 'never upload discoveries, only show traps and coffers i found myself'</comment>
</data>
<data name="ConnectionError_OldVersion" xml:space="preserve">
<value>Ihre Version von Palace Pal ist veraltet, bitte aktualisieren Sie das Plugin mit Hilfe des Plugin Installers.</value>
<comment>Shown if the version is too old to create an account or log in.</comment>
</data>
<!-- Config Window: Deep Dungeons --> <!-- Config Window: Deep Dungeons -->
<data name="ConfigTab_DeepDungeons" xml:space="preserve">
<value>Tiefe Gewölbe</value>
</data>
<data name="Config_Traps_Show" xml:space="preserve">
<value>Fallen anzeigen</value>
</data>
<!-- Config Window: Community --> <!-- Config Window: Community -->
<!-- Config Window: Import --> <!-- Config Window: Import -->
<!-- Config Window: Export --> <!-- Config Window: Export -->

View File

@ -142,6 +142,20 @@
<value>塗りつぶす</value> <value>塗りつぶす</value>
<comment>Whether silver coffers should only be drawn with the circle outline or as filled circle.</comment> <comment>Whether silver coffers should only be drawn with the circle outline or as filled circle.</comment>
</data> </data>
<data name="Config_GoldCoffer_Show" xml:space="preserve">
<value>金の宝箱を表示</value>
</data>
<data name="Config_GoldCoffers_ToolTip" xml:space="preserve">
<value>現在のフロアにある全ての金の宝箱を表示します。
これは他のプレイヤーと同期されず、データは保存されません。</value>
</data>
<data name="Config_GoldCoffer_Color" xml:space="preserve">
<value>金の宝箱の色</value>
</data>
<data name="Config_GoldCoffer_Filled" xml:space="preserve">
<value>塗りつぶす</value>
<comment>Whether gold coffers should only be drawn with the circle outline or as filled circle.</comment>
</data>
<!-- Config Window: Community --> <!-- Config Window: Community -->
<data name="ConfigTab_Community" xml:space="preserve"> <data name="ConfigTab_Community" xml:space="preserve">
<value>コミュニティ</value> <value>コミュニティ</value>

View File

@ -48,6 +48,7 @@
</data> </data>
<data name="Error_LoadFailed" xml:space="preserve"> <data name="Error_LoadFailed" xml:space="preserve">
<value>Plugin could not be loaded: {0}</value> <value>Plugin could not be loaded: {0}</value>
<comment>Shown when the plugin fails to load, with the placeholder filled with the exception message.</comment>
</data> </data>
<data name="Error_WrongRepository" xml:space="preserve"> <data name="Error_WrongRepository" xml:space="preserve">
<value>Please install this plugin from the official repository at {0} to continue using it.</value> <value>Please install this plugin from the official repository at {0} to continue using it.</value>
@ -69,6 +70,10 @@
<value>Unable to fetch statistics.</value> <value>Unable to fetch statistics.</value>
<comment>Shown when /pal stats produces a server-side error, and the statistics window can't be loaded.</comment> <comment>Shown when /pal stats produces a server-side error, and the statistics window can't be loaded.</comment>
</data> </data>
<data name="Error_CommandFailed" xml:space="preserve">
<value>Command could not be executed: {0}</value>
<comment>Shown when '/pal ...' fails, with the placeholder filled with the exception message.</comment>
</data>
<!-- Messages while connecting to the server. These are typically only visible when clicking 'Test connection'. --> <!-- Messages while connecting to the server. These are typically only visible when clicking 'Test connection'. -->
<data name="ConnectionSuccessful" xml:space="preserve"> <data name="ConnectionSuccessful" xml:space="preserve">
@ -141,7 +146,7 @@ When using a Pomander of Safety, all traps are hidden.</value>
<value>Show silver coffers on current floor</value> <value>Show silver coffers on current floor</value>
</data> </data>
<data name="Config_SilverCoffers_ToolTip" xml:space="preserve"> <data name="Config_SilverCoffers_ToolTip" xml:space="preserve">
<value>Shows all the silver coffers visible to you on the current floor. <value>Shows nearby silver coffers (gear upgrades and magicites) on the current floor.
This is not synchronized with other players and not saved between floors/runs.</value> This is not synchronized with other players and not saved between floors/runs.</value>
</data> </data>
<data name="Config_SilverCoffer_Color" xml:space="preserve"> <data name="Config_SilverCoffer_Color" xml:space="preserve">
@ -151,6 +156,20 @@ This is not synchronized with other players and not saved between floors/runs.</
<value>Draw filled</value> <value>Draw filled</value>
<comment>Whether silver coffers should only be drawn with the circle outline or as filled circle.</comment> <comment>Whether silver coffers should only be drawn with the circle outline or as filled circle.</comment>
</data> </data>
<data name="Config_GoldCoffer_Show" xml:space="preserve">
<value>Show gold coffers on current floor</value>
</data>
<data name="Config_GoldCoffers_ToolTip" xml:space="preserve">
<value>Shows nearby gold coffers (containing pomanders) on the current floor.
This is not synchronized with other players and not saved between floors/runs.</value>
</data>
<data name="Config_GoldCoffer_Color" xml:space="preserve">
<value>Gold Coffer color</value>
</data>
<data name="Config_GoldCoffer_Filled" xml:space="preserve">
<value>Draw filled</value>
<comment>Whether gold coffers should only be drawn with the circle outline or as filled circle.</comment>
</data>
<!-- Config Window: Community --> <!-- Config Window: Community -->
<data name="ConfigTab_Community" xml:space="preserve"> <data name="ConfigTab_Community" xml:space="preserve">

View File

@ -15,6 +15,7 @@ dotnet ef migrations add MigrationName --configuration EF
``` ```
To rebuild the compiled model: To rebuild the compiled model:
```shell ```shell
dotnet ef dbcontext optimize --output-dir Database/Compiled --namespace Pal.Client.Database.Compiled --configuration EF dotnet ef dbcontext optimize --output-dir Database/Compiled --namespace Pal.Client.Database.Compiled --configuration EF
``` ```

View File

@ -1,9 +1,8 @@
namespace Pal.Client.Rendering namespace Pal.Client.Rendering;
internal enum ELayer
{ {
internal enum ELayer
{
TrapHoard, TrapHoard,
RegularCoffers, RegularCoffers,
Test, Test,
}
} }

View File

@ -1,9 +1,8 @@
namespace Pal.Client.Rendering namespace Pal.Client.Rendering;
public interface IRenderElement
{ {
public interface IRenderElement
{
bool IsValid { get; } bool IsValid { get; }
uint Color { get; set; } bool Enabled { get; set; }
}
} }

View File

@ -3,18 +3,17 @@ using System.Numerics;
using Pal.Client.Configuration; using Pal.Client.Configuration;
using Pal.Client.Floors; using Pal.Client.Floors;
namespace Pal.Client.Rendering namespace Pal.Client.Rendering;
internal interface IRenderer
{ {
internal interface IRenderer
{
ERenderer GetConfigValue(); ERenderer GetConfigValue();
void SetLayer(ELayer layer, IReadOnlyList<IRenderElement> elements); void SetLayer(ELayer layer, IReadOnlyList<IRenderElement> elements);
void ResetLayer(ELayer layer); void ResetLayer(ELayer layer);
IRenderElement CreateElement(MemoryLocation.EType type, Vector3 pos, uint color, bool fill = false); IRenderElement CreateElement(MemoryLocation.EType type, Vector3 pos, bool enabled, uint color, bool fill = false);
void DrawDebugItems(uint trapColor, uint hoardColor); void DrawDebugItems(uint trapColor, uint hoardColor);
}
} }

View File

@ -1,10 +1,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using Pal.Client.Floors; 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<MemoryLocation.EType, MarkerConfig> MarkerConfigs = new() private static readonly Dictionary<MemoryLocation.EType, MarkerConfig> MarkerConfigs = new()
@ -12,6 +12,7 @@ namespace Pal.Client.Rendering
{ MemoryLocation.EType.Trap, new MarkerConfig { Radius = 1.7f } }, { MemoryLocation.EType.Trap, new MarkerConfig { Radius = 1.7f } },
{ MemoryLocation.EType.Hoard, new MarkerConfig { Radius = 1.7f, OffsetY = -0.03f } }, { MemoryLocation.EType.Hoard, new MarkerConfig { Radius = 1.7f, OffsetY = -0.03f } },
{ MemoryLocation.EType.SilverCoffer, new MarkerConfig { Radius = 1f } }, { MemoryLocation.EType.SilverCoffer, new MarkerConfig { Radius = 1f } },
{ MemoryLocation.EType.GoldCoffer, new MarkerConfig { Radius = 1f } },
}; };
public float OffsetY { get; private init; } public float OffsetY { get; private init; }
@ -19,5 +20,4 @@ namespace Pal.Client.Rendering
public static MarkerConfig ForType(MemoryLocation.EType type) => public static MarkerConfig ForType(MemoryLocation.EType type) =>
MarkerConfigs.GetValueOrDefault(type, EmptyConfig); MarkerConfigs.GetValueOrDefault(type, EmptyConfig);
}
} }

View File

@ -6,10 +6,10 @@ using Microsoft.Extensions.Logging;
using Pal.Client.Configuration; using Pal.Client.Configuration;
using Pal.Client.Floors; using Pal.Client.Floors;
namespace Pal.Client.Rendering namespace Pal.Client.Rendering;
internal sealed class RenderAdapter : IRenderer, IDisposable
{ {
internal sealed class RenderAdapter : IRenderer, IDisposable
{
private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly ILogger<RenderAdapter> _logger; private readonly ILogger<RenderAdapter> _logger;
private readonly IPalacePalConfiguration _configuration; private readonly IPalacePalConfiguration _configuration;
@ -27,6 +27,8 @@ namespace Pal.Client.Rendering
_implementation = Recreate(null); _implementation = Recreate(null);
} }
public bool RequireRedraw { get; set; }
private IRenderer Recreate(ERenderer? currentRenderer) private IRenderer Recreate(ERenderer? currentRenderer)
{ {
ERenderer targetRenderer = _configuration.Renderer.SelectedRenderer; ERenderer targetRenderer = _configuration.Renderer.SelectedRenderer;
@ -46,6 +48,7 @@ namespace Pal.Client.Rendering
public void ConfigUpdated() public void ConfigUpdated()
{ {
_implementation = Recreate(_implementation.GetConfigValue()); _implementation = Recreate(_implementation.GetConfigValue());
RequireRedraw = true;
} }
public void Dispose() public void Dispose()
@ -57,8 +60,9 @@ namespace Pal.Client.Rendering
public void ResetLayer(ELayer layer) public void ResetLayer(ELayer layer)
=> _implementation.ResetLayer(layer); => _implementation.ResetLayer(layer);
public IRenderElement CreateElement(MemoryLocation.EType type, Vector3 pos, uint color, bool fill = false) public IRenderElement CreateElement(MemoryLocation.EType type, Vector3 pos, bool enabled, uint color,
=> _implementation.CreateElement(type, pos, color, fill); bool fill = false)
=> _implementation.CreateElement(type, pos, enabled, color, fill);
public ERenderer GetConfigValue() public ERenderer GetConfigValue()
=> throw new NotImplementedException(); => throw new NotImplementedException();
@ -71,5 +75,4 @@ namespace Pal.Client.Rendering
if (_implementation is SimpleRenderer sr) if (_implementation is SimpleRenderer sr)
sr.DrawLayers(); sr.DrawLayers();
} }
}
} }

View File

@ -1,8 +1,6 @@
namespace Pal.Client.Rendering namespace Pal.Client.Rendering;
internal static class RenderData
{ {
internal static class RenderData
{
public static readonly uint ColorInvisible = 0;
public static readonly long TestLayerTimeout = 10_000; public static readonly long TestLayerTimeout = 10_000;
}
} }

View File

@ -1,36 +1,34 @@
using Dalamud.Interface; using System;
using ImGuiNET;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Dalamud.Game.ClientState; using Dalamud.Interface.Utility;
using Dalamud.Game.Gui; using Dalamud.Plugin.Services;
using ImGuiNET;
using Pal.Client.Configuration; using Pal.Client.Configuration;
using Pal.Client.DependencyInjection;
using Pal.Client.Floors; using Pal.Client.Floors;
namespace Pal.Client.Rendering namespace Pal.Client.Rendering;
/// <summary>
/// Simple renderer that only draws basic stuff.
///
/// This is based on what SliceIsRight uses, and what PalacePal used before it was
/// remade into PalacePal (which is the third or fourth iteration on the same idea
/// I made, just with a clear vision).
/// </summary>
internal sealed class SimpleRenderer : IRenderer, IDisposable
{ {
/// <summary>
/// Simple renderer that only draws basic stuff.
///
/// This is based on what SliceIsRight uses, and what PalacePal used before it was
/// remade into PalacePal (which is the third or fourth iteration on the same idea
/// I made, just with a clear vision).
/// </summary>
internal sealed class SimpleRenderer : IRenderer, IDisposable
{
private const int SegmentCount = 20; private const int SegmentCount = 20;
private readonly ClientState _clientState; private readonly IClientState _clientState;
private readonly GameGui _gameGui; private readonly IGameGui _gameGui;
private readonly IPalacePalConfiguration _configuration; private readonly IPalacePalConfiguration _configuration;
private readonly TerritoryState _territoryState; private readonly TerritoryState _territoryState;
private readonly ConcurrentDictionary<ELayer, SimpleLayer> _layers = new(); private readonly ConcurrentDictionary<ELayer, SimpleLayer> _layers = new();
public SimpleRenderer(ClientState clientState, GameGui gameGui, IPalacePalConfiguration configuration, public SimpleRenderer(IClientState clientState, IGameGui gameGui, IPalacePalConfiguration configuration,
TerritoryState territoryState) TerritoryState territoryState)
{ {
_clientState = clientState; _clientState = clientState;
@ -54,13 +52,15 @@ namespace Pal.Client.Rendering
l.Dispose(); l.Dispose();
} }
public IRenderElement CreateElement(MemoryLocation.EType type, Vector3 pos, uint color, bool fill = false) public IRenderElement CreateElement(MemoryLocation.EType type, Vector3 pos, bool enabled, uint color,
bool fill = false)
{ {
var config = MarkerConfig.ForType(type); var config = MarkerConfig.ForType(type);
return new SimpleElement return new SimpleElement
{ {
Type = type, Type = type,
Position = pos + new Vector3(0, config.OffsetY, 0), Position = pos + new Vector3(0, config.OffsetY, 0),
Enabled = enabled,
Color = color, Color = color,
Radius = config.Radius, Radius = config.Radius,
Fill = fill, Fill = fill,
@ -77,10 +77,12 @@ namespace Pal.Client.Rendering
(SimpleElement)CreateElement( (SimpleElement)CreateElement(
MemoryLocation.EType.Trap, MemoryLocation.EType.Trap,
_clientState.LocalPlayer?.Position ?? default, _clientState.LocalPlayer?.Position ?? default,
true,
trapColor), trapColor),
(SimpleElement)CreateElement( (SimpleElement)CreateElement(
MemoryLocation.EType.Hoard, MemoryLocation.EType.Hoard,
_clientState.LocalPlayer?.Position ?? default, _clientState.LocalPlayer?.Position ?? default,
true,
hoardColor) hoardColor)
}, },
ExpiresAt = Environment.TickCount64 + RenderData.TestLayerTimeout ExpiresAt = Environment.TickCount64 + RenderData.TestLayerTimeout
@ -120,7 +122,7 @@ namespace Pal.Client.Rendering
private void Draw(SimpleElement e) private void Draw(SimpleElement e)
{ {
if (e.Color == RenderData.ColorInvisible) if (!e.Enabled)
return; return;
switch (e.Type) switch (e.Type)
@ -181,7 +183,7 @@ namespace Pal.Client.Rendering
public required IReadOnlyList<SimpleElement> Elements { get; init; } public required IReadOnlyList<SimpleElement> Elements { get; init; }
public long ExpiresAt { get; init; } = long.MaxValue; public long ExpiresAt { get; init; } = long.MaxValue;
public bool IsValid(ClientState clientState) => public bool IsValid(IClientState clientState) =>
TerritoryType == clientState.TerritoryType && ExpiresAt >= Environment.TickCount64; TerritoryType == clientState.TerritoryType && ExpiresAt >= Environment.TickCount64;
public void Dispose() public void Dispose()
@ -196,9 +198,9 @@ namespace Pal.Client.Rendering
public bool IsValid { get; set; } = true; public bool IsValid { get; set; } = true;
public required MemoryLocation.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 bool Enabled { get; set; }
public required uint Color { get; set; } public required uint Color { get; set; }
public required float Radius { get; init; } public required float Radius { get; init; }
public required bool Fill { get; init; } public required bool Fill { get; init; }
} }
}
} }

View File

@ -1,37 +1,37 @@
using Dalamud.Plugin; using System;
using ECommons;
using ECommons.Reflection;
using ECommons.Schedulers;
using ECommons.SplatoonAPI;
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Reflection; using System.Reflection;
using Dalamud.Game.ClientState; using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using ECommons;
using ECommons.Reflection;
using ECommons.Schedulers;
using ECommons.SplatoonAPI;
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.Floors;
namespace Pal.Client.Rendering namespace Pal.Client.Rendering;
internal sealed class SplatoonRenderer : IRenderer, IDisposable
{ {
internal sealed class SplatoonRenderer : IRenderer, IDisposable
{
private const long OnTerritoryChange = -2; private const long OnTerritoryChange = -2;
private readonly ILogger<SplatoonRenderer> _logger; private readonly ILogger<SplatoonRenderer> _logger;
private readonly DebugState _debugState; private readonly DebugState _debugState;
private readonly ClientState _clientState; private readonly IClientState _clientState;
private readonly Chat _chat; private readonly Chat _chat;
public SplatoonRenderer( public SplatoonRenderer(
ILogger<SplatoonRenderer> logger, ILogger<SplatoonRenderer> logger,
DalamudPluginInterface pluginInterface, IDalamudPluginInterface pluginInterface,
IDalamudPlugin dalamudPlugin, IDalamudPlugin dalamudPlugin,
DebugState debugState, DebugState debugState,
ClientState clientState, IClientState clientState,
Chat chat) Chat chat)
{ {
_logger = logger; _logger = logger;
@ -80,7 +80,7 @@ namespace Pal.Client.Rendering
private string ToLayerName(ELayer layer) private string ToLayerName(ELayer layer)
=> $"PalacePal.{layer}"; => $"PalacePal.{layer}";
public IRenderElement CreateElement(MemoryLocation.EType type, Vector3 pos, uint color, bool fill = false) public IRenderElement CreateElement(MemoryLocation.EType type, Vector3 pos, bool enabled, 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)
@ -96,6 +96,7 @@ namespace Pal.Client.Rendering
FillStep = 1, FillStep = 1,
color = color, color = color,
thicc = 2, thicc = 2,
Enabled = enabled,
}; };
return new SplatoonElement(this, element); return new SplatoonElement(this, element);
} }
@ -111,8 +112,8 @@ namespace Pal.Client.Rendering
var elements = new List<IRenderElement> var elements = new List<IRenderElement>
{ {
CreateElement(MemoryLocation.EType.Trap, pos.Value, trapColor), CreateElement(MemoryLocation.EType.Trap, pos.Value, true, trapColor),
CreateElement(MemoryLocation.EType.Hoard, pos.Value, hoardColor), CreateElement(MemoryLocation.EType.Hoard, pos.Value, true, hoardColor),
}; };
if (!Splatoon.AddDynamicElements(ToLayerName(ELayer.Test), if (!Splatoon.AddDynamicElements(ToLayerName(ELayer.Test),
@ -186,11 +187,10 @@ namespace Pal.Client.Rendering
public bool IsValid => !_renderer.IsDisposed && Delegate.IsValid(); public bool IsValid => !_renderer.IsDisposed && Delegate.IsValid();
public uint Color public bool Enabled
{ {
get => Delegate.color; get => Delegate.Enabled;
set => Delegate.color = value; set => Delegate.Enabled = value;
}
} }
} }
} }

View File

@ -2,10 +2,10 @@
using Dalamud.Logging; using Dalamud.Logging;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Pal.Client.Scheduled namespace Pal.Client.Scheduled;
internal interface IQueueOnFrameworkThread
{ {
internal interface IQueueOnFrameworkThread
{
internal interface IHandler internal interface IHandler
{ {
void RunIfCompatible(IQueueOnFrameworkThread queued, ref bool recreateLayout); void RunIfCompatible(IQueueOnFrameworkThread queued, ref bool recreateLayout);
@ -36,5 +36,4 @@ namespace Pal.Client.Scheduled
} }
} }
} }
}
} }

View File

@ -4,10 +4,10 @@ using Pal.Client.DependencyInjection;
using Pal.Client.Floors; using Pal.Client.Floors;
using Pal.Client.Rendering; using Pal.Client.Rendering;
namespace Pal.Client.Scheduled namespace Pal.Client.Scheduled;
internal sealed class QueuedConfigUpdate : IQueueOnFrameworkThread
{ {
internal sealed class QueuedConfigUpdate : IQueueOnFrameworkThread
{
internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedConfigUpdate> internal sealed class Handler : IQueueOnFrameworkThread.Handler<QueuedConfigUpdate>
{ {
private readonly RenderAdapter _renderAdapter; private readonly RenderAdapter _renderAdapter;
@ -22,9 +22,7 @@ namespace Pal.Client.Scheduled
protected override void Run(QueuedConfigUpdate queued, ref bool recreateLayout) protected override void Run(QueuedConfigUpdate queued, ref bool recreateLayout)
{ {
// TODO filter stuff if offline
_renderAdapter.ConfigUpdated(); _renderAdapter.ConfigUpdated();
} }
} }
}
} }

View File

@ -1,19 +1,19 @@
using Account; using System;
using Pal.Common;
using System;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Export;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Pal.Client.Database; using Pal.Client.Database;
using Pal.Client.DependencyInjection; using Pal.Client.DependencyInjection;
using Pal.Client.Properties; using Pal.Client.Properties;
using Pal.Client.Windows; using Pal.Client.Windows;
using Pal.Common;
namespace Pal.Client.Scheduled namespace Pal.Client.Scheduled;
internal sealed class QueuedImport : IQueueOnFrameworkThread
{ {
internal sealed class QueuedImport : IQueueOnFrameworkThread
{
private ExportRoot Export { get; } private ExportRoot Export { get; }
private Guid ExportId { get; set; } private Guid ExportId { get; set; }
private int ImportedTraps { get; set; } private int ImportedTraps { get; set; }
@ -119,5 +119,4 @@ namespace Pal.Client.Scheduled
return true; return true;
} }
} }
}
} }

View File

@ -11,10 +11,10 @@ using Pal.Client.Floors.Tasks;
using Pal.Client.Net; using Pal.Client.Net;
using Pal.Common; using Pal.Common;
namespace Pal.Client.Scheduled namespace Pal.Client.Scheduled;
internal sealed class QueuedSyncResponse : IQueueOnFrameworkThread
{ {
internal sealed class QueuedSyncResponse : IQueueOnFrameworkThread
{
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; }
@ -141,21 +141,20 @@ namespace Pal.Client.Scheduled
} }
} }
} }
} }
public enum ESyncState public enum ESyncState
{ {
NotAttempted, NotAttempted,
NotNeeded, NotNeeded,
Started, Started,
Complete, Complete,
Failed, Failed,
} }
public enum SyncType public enum SyncType
{ {
Upload, Upload,
Download, Download,
MarkSeen, MarkSeen,
}
} }

View File

@ -7,10 +7,10 @@ using Pal.Client.Floors;
using Pal.Client.Windows; using Pal.Client.Windows;
using Pal.Common; using Pal.Common;
namespace Pal.Client.Scheduled namespace Pal.Client.Scheduled;
internal sealed class QueuedUndoImport : IQueueOnFrameworkThread
{ {
internal sealed class QueuedUndoImport : IQueueOnFrameworkThread
{
public QueuedUndoImport(Guid exportId) public QueuedUndoImport(Guid exportId)
{ {
ExportId = exportId; ExportId = exportId;
@ -38,5 +38,4 @@ namespace Pal.Client.Scheduled
_configWindow.UpdateLastImport(); _configWindow.UpdateLastImport();
} }
} }
}
} }

View File

@ -1,17 +1,17 @@
using System; using System;
using System.Numerics;
using Dalamud.Interface.Colors; using Dalamud.Interface.Colors;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using ECommons; using ECommons;
using ImGuiNET; using ImGuiNET;
using System.Numerics;
using Pal.Client.Configuration; using Pal.Client.Configuration;
using Pal.Client.Extensions; using Pal.Client.Extensions;
using Pal.Client.Properties; using Pal.Client.Properties;
namespace Pal.Client.Windows namespace Pal.Client.Windows;
internal sealed class AgreementWindow : Window, IDisposable, ILanguageChanged
{ {
internal sealed class AgreementWindow : Window, IDisposable, ILanguageChanged
{
private const string WindowId = "###PalPalaceAgreement"; private const string WindowId = "###PalPalaceAgreement";
private readonly WindowSystem _windowSystem; private readonly WindowSystem _windowSystem;
private readonly ConfigurationManager _configurationManager; private readonly ConfigurationManager _configurationManager;
@ -99,7 +99,6 @@ namespace Pal.Client.Windows
ImGui.Separator(); ImGui.Separator();
if (ImGui.Button(Localization.Agreement_ViewPluginAndServerSourceCode)) if (ImGui.Button(Localization.Agreement_ViewPluginAndServerSourceCode))
GenericHelpers.ShellStart("https://github.com/carvelli/PalPalace"); GenericHelpers.ShellStart("https://git.carvel.li/liza/PalacePal");
}
} }
} }

View File

@ -1,32 +1,34 @@
using Account; using System;
using Dalamud.Interface;
using Dalamud.Interface.Components;
using Dalamud.Interface.ImGuiFileDialog;
using Dalamud.Interface.Windowing;
using ECommons;
using Google.Protobuf;
using ImGuiNET;
using Pal.Client.Net;
using Pal.Client.Rendering;
using Pal.Client.Scheduled;
using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dalamud.Interface;
using Dalamud.Interface.Components;
using Dalamud.Interface.ImGuiFileDialog;
using Dalamud.Interface.Windowing;
using ECommons;
using Export;
using Google.Protobuf;
using ImGuiNET;
using LLib;
using LLib.ImGui;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Pal.Client.Extensions;
using Pal.Client.Properties;
using Pal.Client.Configuration; 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.Floors; using Pal.Client.Floors;
using Pal.Client.Net;
using Pal.Client.Properties;
using Pal.Client.Rendering;
using Pal.Client.Scheduled;
namespace Pal.Client.Windows namespace Pal.Client.Windows;
internal sealed class ConfigWindow : LWindow, ILanguageChanged, IDisposable
{ {
internal sealed class ConfigWindow : Window, ILanguageChanged, IDisposable
{
private const string WindowId = "###PalPalaceConfig"; private const string WindowId = "###PalPalaceConfig";
private readonly ILogger<ConfigWindow> _logger; private readonly ILogger<ConfigWindow> _logger;
@ -47,6 +49,7 @@ namespace Pal.Client.Windows
private ConfigurableMarker _trapConfig = new(); private ConfigurableMarker _trapConfig = new();
private ConfigurableMarker _hoardConfig = new(); private ConfigurableMarker _hoardConfig = new();
private ConfigurableMarker _silverConfig = new(); private ConfigurableMarker _silverConfig = new();
private ConfigurableMarker _goldConfig = new();
private string? _connectionText; private string? _connectionText;
private bool _switchToCommunityTab; private bool _switchToCommunityTab;
@ -96,6 +99,12 @@ namespace Pal.Client.Windows
Position = new Vector2(300, 300); Position = new Vector2(300, 300);
PositionCondition = ImGuiCond.FirstUseEver; PositionCondition = ImGuiCond.FirstUseEver;
SizeConstraints = new WindowSizeConstraints
{
MinimumSize = new Vector2(300, 300),
MaximumSize = new Vector2(9999, 9999),
};
_importDialog = new FileDialogManager _importDialog = new FileDialogManager
{ AddedWindowFlags = ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoDocking }; { AddedWindowFlags = ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoDocking };
_exportDialog = new FileDialogManager _exportDialog = new FileDialogManager
@ -124,6 +133,7 @@ namespace Pal.Client.Windows
_trapConfig = new ConfigurableMarker(_configuration.DeepDungeons.Traps); _trapConfig = new ConfigurableMarker(_configuration.DeepDungeons.Traps);
_hoardConfig = new ConfigurableMarker(_configuration.DeepDungeons.HoardCoffers); _hoardConfig = new ConfigurableMarker(_configuration.DeepDungeons.HoardCoffers);
_silverConfig = new ConfigurableMarker(_configuration.DeepDungeons.SilverCoffers); _silverConfig = new ConfigurableMarker(_configuration.DeepDungeons.SilverCoffers);
_goldConfig = new ConfigurableMarker(_configuration.DeepDungeons.GoldCoffers);
_connectionText = null; _connectionText = null;
UpdateLastImport(); UpdateLastImport();
@ -162,6 +172,7 @@ namespace Pal.Client.Windows
_configuration.DeepDungeons.Traps = _trapConfig.Build(); _configuration.DeepDungeons.Traps = _trapConfig.Build();
_configuration.DeepDungeons.HoardCoffers = _hoardConfig.Build(); _configuration.DeepDungeons.HoardCoffers = _hoardConfig.Build();
_configuration.DeepDungeons.SilverCoffers = _silverConfig.Build(); _configuration.DeepDungeons.SilverCoffers = _silverConfig.Build();
_configuration.DeepDungeons.GoldCoffers = _goldConfig.Build();
_configurationManager.Save(_configuration); _configurationManager.Save(_configuration);
@ -174,6 +185,7 @@ namespace Pal.Client.Windows
{ {
if (ImGui.BeginTabItem($"{Localization.ConfigTab_DeepDungeons}###TabDeepDungeons")) if (ImGui.BeginTabItem($"{Localization.ConfigTab_DeepDungeons}###TabDeepDungeons"))
{ {
ImGui.PushID("trap");
ImGui.Checkbox(Localization.Config_Traps_Show, ref _trapConfig.Show); ImGui.Checkbox(Localization.Config_Traps_Show, ref _trapConfig.Show);
ImGui.Indent(); ImGui.Indent();
ImGui.BeginDisabled(!_trapConfig.Show); ImGui.BeginDisabled(!_trapConfig.Show);
@ -184,9 +196,11 @@ namespace Pal.Client.Windows
ImGuiComponents.HelpMarker(Localization.Config_Traps_HideImpossible_ToolTip); ImGuiComponents.HelpMarker(Localization.Config_Traps_HideImpossible_ToolTip);
ImGui.EndDisabled(); ImGui.EndDisabled();
ImGui.Unindent(); ImGui.Unindent();
ImGui.PopID();
ImGui.Separator(); ImGui.Separator();
ImGui.PushID("hoard");
ImGui.Checkbox(Localization.Config_HoardCoffers_Show, ref _hoardConfig.Show); ImGui.Checkbox(Localization.Config_HoardCoffers_Show, ref _hoardConfig.Show);
ImGui.Indent(); ImGui.Indent();
ImGui.BeginDisabled(!_hoardConfig.Show); ImGui.BeginDisabled(!_hoardConfig.Show);
@ -199,9 +213,11 @@ namespace Pal.Client.Windows
ImGuiComponents.HelpMarker(Localization.Config_HoardCoffers_HideImpossible_ToolTip); ImGuiComponents.HelpMarker(Localization.Config_HoardCoffers_HideImpossible_ToolTip);
ImGui.EndDisabled(); ImGui.EndDisabled();
ImGui.Unindent(); ImGui.Unindent();
ImGui.PopID();
ImGui.Separator(); ImGui.Separator();
ImGui.PushID("silver");
ImGui.Checkbox(Localization.Config_SilverCoffer_Show, ref _silverConfig.Show); ImGui.Checkbox(Localization.Config_SilverCoffer_Show, ref _silverConfig.Show);
ImGuiComponents.HelpMarker(Localization.Config_SilverCoffers_ToolTip); ImGuiComponents.HelpMarker(Localization.Config_SilverCoffers_ToolTip);
ImGui.Indent(); ImGui.Indent();
@ -212,6 +228,22 @@ namespace Pal.Client.Windows
ImGui.Checkbox(Localization.Config_SilverCoffer_Filled, ref _silverConfig.Fill); ImGui.Checkbox(Localization.Config_SilverCoffer_Filled, ref _silverConfig.Fill);
ImGui.EndDisabled(); ImGui.EndDisabled();
ImGui.Unindent(); ImGui.Unindent();
ImGui.PopID();
ImGui.Separator();
ImGui.PushID("gold");
ImGui.Checkbox(Localization.Config_GoldCoffer_Show, ref _goldConfig.Show);
ImGuiComponents.HelpMarker(Localization.Config_GoldCoffers_ToolTip);
ImGui.Indent();
ImGui.BeginDisabled(!_goldConfig.Show);
ImGui.Spacing();
ImGui.ColorEdit4(Localization.Config_GoldCoffer_Color, ref _goldConfig.Color,
ImGuiColorEditFlags.NoInputs);
ImGui.Checkbox(Localization.Config_GoldCoffer_Filled, ref _goldConfig.Fill);
ImGui.EndDisabled();
ImGui.Unindent();
ImGui.PopID();
ImGui.Separator(); ImGui.Separator();
@ -260,11 +292,13 @@ namespace Pal.Client.Windows
ImGui.TextWrapped(Localization.Config_ImportExplanation1); ImGui.TextWrapped(Localization.Config_ImportExplanation1);
ImGui.TextWrapped(Localization.Config_ImportExplanation2); ImGui.TextWrapped(Localization.Config_ImportExplanation2);
ImGui.TextWrapped(Localization.Config_ImportExplanation3); ImGui.TextWrapped(Localization.Config_ImportExplanation3);
/* FIXME
ImGui.Separator(); ImGui.Separator();
ImGui.TextWrapped(string.Format(Localization.Config_ImportDownloadLocation, ImGui.TextWrapped(string.Format(Localization.Config_ImportDownloadLocation,
"https://github.com/carvelli/PalacePal/releases/")); "https://github.com/carvelli/PalacePal/releases/"));
if (ImGui.Button(Localization.Config_Import_VisitGitHub)) if (ImGui.Button(Localization.Config_Import_VisitGitHub))
GenericHelpers.ShellStart("https://github.com/carvelli/PalacePal/releases/latest"); GenericHelpers.ShellStart("https://github.com/carvelli/PalacePal/releases/latest");
*/
ImGui.Separator(); ImGui.Separator();
ImGui.Text(Localization.Config_SelectImportFile); ImGui.Text(Localization.Config_SelectImportFile);
ImGui.SameLine(); ImGui.SameLine();
@ -408,6 +442,15 @@ namespace Pal.Client.Windows
$"{silverCoffers} silver coffer{(silverCoffers == 1 ? "" : "s")} visible on current floor"); $"{silverCoffers} silver coffer{(silverCoffers == 1 ? "" : "s")} visible on current floor");
} }
if (_goldConfig.Show)
{
int goldCoffers =
_floorService.EphemeralLocations.Count(x =>
x.Type == MemoryLocation.EType.GoldCoffer);
ImGui.Text(
$"{goldCoffers} silver coffer{(goldCoffers == 1 ? "" : "s")} visible on current floor");
}
ImGui.Text($"Pomander of Sight: {_territoryState.PomanderOfSight}"); ImGui.Text($"Pomander of Sight: {_territoryState.PomanderOfSight}");
ImGui.Text($"Pomander of Intuition: {_territoryState.PomanderOfIntuition}"); ImGui.Text($"Pomander of Intuition: {_territoryState.PomanderOfIntuition}");
} }
@ -542,5 +585,4 @@ namespace Pal.Client.Windows
}; };
} }
} }
}
} }

View File

@ -1,17 +1,17 @@
using Dalamud.Interface.Windowing; using System;
using ImGuiNET;
using Pal.Common;
using Palace;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Dalamud.Interface.Windowing;
using ImGuiNET;
using Pal.Client.Properties; using Pal.Client.Properties;
using Pal.Common;
using Palace;
namespace Pal.Client.Windows namespace Pal.Client.Windows;
internal sealed class StatisticsWindow : Window, IDisposable, ILanguageChanged
{ {
internal sealed class StatisticsWindow : Window, IDisposable, ILanguageChanged
{
private const string WindowId = "###PalacePalStats"; private const string WindowId = "###PalacePalStats";
private readonly WindowSystem _windowSystem; private readonly WindowSystem _windowSystem;
private readonly SortedDictionary<ETerritoryType, TerritoryStatistics> _territoryStatistics = new(); private readonly SortedDictionary<ETerritoryType, TerritoryStatistics> _territoryStatistics = new();
@ -49,6 +49,8 @@ namespace Pal.Client.Windows
ETerritoryType.Palace_191_200); ETerritoryType.Palace_191_200);
DrawDungeonStats("Heaven on High", Localization.HeavenOnHigh, ETerritoryType.HeavenOnHigh_1_10, DrawDungeonStats("Heaven on High", Localization.HeavenOnHigh, ETerritoryType.HeavenOnHigh_1_10,
ETerritoryType.HeavenOnHigh_91_100); ETerritoryType.HeavenOnHigh_91_100);
DrawDungeonStats("Eureka Orthos", Localization.EurekaOrthos, ETerritoryType.EurekaOrthos_1_10,
ETerritoryType.EurekaOrthos_91_100);
} }
} }
@ -120,5 +122,4 @@ namespace Pal.Client.Windows
TerritoryName = territoryName; TerritoryName = territoryName;
} }
} }
}
} }

View File

@ -0,0 +1,344 @@
{
"version": 1,
"dependencies": {
"net8.0-windows7.0": {
"Dalamud.Extensions.MicrosoftLogging": {
"type": "Direct",
"requested": "[4.0.1, )",
"resolved": "4.0.1",
"contentHash": "fMEL2ajtF/30SBBku7vMyG0yye5eHN/A9fgT//1CEjUth/Wz2CYco5Ehye21T8KN1IuAPwoqJuu49rB71j+8ug==",
"dependencies": {
"Microsoft.Extensions.Logging": "8.0.0"
}
},
"DalamudPackager": {
"type": "Direct",
"requested": "[2.1.13, )",
"resolved": "2.1.13",
"contentHash": "rMN1omGe8536f4xLMvx9NwfvpAc9YFFfeXJ1t4P4PE6Gu8WCIoFliR1sh07hM+bfODmesk/dvMbji7vNI+B/pQ=="
},
"DotNet.ReproducibleBuilds": {
"type": "Direct",
"requested": "[1.1.1, )",
"resolved": "1.1.1",
"contentHash": "+H2t/t34h6mhEoUvHi8yGXyuZ2GjSovcGYehJrS2MDm2XgmPfZL2Sdxg+uL2lKgZ4M6tTwKHIlxOob2bgh0NRQ==",
"dependencies": {
"Microsoft.SourceLink.AzureRepos.Git": "1.1.1",
"Microsoft.SourceLink.Bitbucket.Git": "1.1.1",
"Microsoft.SourceLink.GitHub": "1.1.1",
"Microsoft.SourceLink.GitLab": "1.1.1"
}
},
"Google.Protobuf": {
"type": "Direct",
"requested": "[3.27.2, )",
"resolved": "3.27.2",
"contentHash": "0wdgA3LO9mBS477jieBFs4pU1sWhVtwv/P+i9nAEiFDQyUA7PPHDBbJL1CeqYtV18jLiq9og4n7wSVCO171OBg=="
},
"Grpc.Net.Client": {
"type": "Direct",
"requested": "[2.63.0, )",
"resolved": "2.63.0",
"contentHash": "847zG24daOP1242OpbnjhbKtplH/EfV/76QReQA3cbS5SL78uIXsWMe9IN9JlIb4+kT3eE4fjMCXTn8BAQ91Ng==",
"dependencies": {
"Grpc.Net.Common": "2.63.0",
"Microsoft.Extensions.Logging.Abstractions": "6.0.0"
}
},
"Grpc.Tools": {
"type": "Direct",
"requested": "[2.64.0, )",
"resolved": "2.64.0",
"contentHash": "W5RrhDFHUhioASktxfuDs5fTjWUxwegljZAig9zFL8nWNskeyQA6OXN2choWKYxGrljer25VqCJCMbWz7XHvqg=="
},
"Microsoft.EntityFrameworkCore.Sqlite": {
"type": "Direct",
"requested": "[8.0.6, )",
"resolved": "8.0.6",
"contentHash": "nC4cZN4zReTb22qd9WDU0eDmlXvkyf2g2pqQ3VIHJbkpJcdWSY/PDgwGpbpShsVcAjXbkjGiUcv9aGwa61xQPw==",
"dependencies": {
"Microsoft.EntityFrameworkCore.Sqlite.Core": "8.0.6",
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.6"
}
},
"Microsoft.Extensions.Logging": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "8.0.0",
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
"Microsoft.Extensions.Options": "8.0.0"
}
},
"Microsoft.SourceLink.Gitea": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "KOBodmDnlWGIqZt2hT47Q69TIoGhIApDVLCyyj9TT5ct8ju16AbHYcB4XeknoHX562wO1pMS/1DfBIZK+V+sxg==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "8.0.0",
"Microsoft.SourceLink.Common": "8.0.0"
}
},
"System.Security.Cryptography.ProtectedData": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg=="
},
"Grpc.Core.Api": {
"type": "Transitive",
"resolved": "2.63.0",
"contentHash": "t3+/MF8AxIqKq5UmPB9EWAnM9C/+lXOB8TRFfeVMDntf6dekfJmjpKDebaT4t2bbuwVwwvthxxox9BuGr59kYA=="
},
"Grpc.Net.Common": {
"type": "Transitive",
"resolved": "2.63.0",
"contentHash": "RLt6p31ZMsXRcHNeu1dQuIFLYZvnwP6LUzoDPlV3KoR4w9btmwrXIvz9Jbp1SOmxW7nXw9zShAeIt5LsqFAx5w==",
"dependencies": {
"Grpc.Core.Api": "2.63.0"
}
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
},
"Microsoft.Data.Sqlite.Core": {
"type": "Transitive",
"resolved": "8.0.6",
"contentHash": "umhZ0ZF2RI81rGFTnYmCxI+Euj4Aqe/6Y4+8CxN9OVJNGDNIqB5laJ3wxQTU8zXCcm2k9F7FL+/6RVoOT4z1Fw==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.6"
}
},
"Microsoft.EntityFrameworkCore": {
"type": "Transitive",
"resolved": "8.0.6",
"contentHash": "Ms5e5QuBAjVIuQsGumeLvkgMiOpnj6wxPvwBIoe1NfTkseWK4NZYztnhgDlpkCPkrUmJEXLv69kl349Ours30Q==",
"dependencies": {
"Microsoft.EntityFrameworkCore.Abstractions": "8.0.6",
"Microsoft.EntityFrameworkCore.Analyzers": "8.0.6",
"Microsoft.Extensions.Caching.Memory": "8.0.0",
"Microsoft.Extensions.Logging": "8.0.0"
}
},
"Microsoft.EntityFrameworkCore.Abstractions": {
"type": "Transitive",
"resolved": "8.0.6",
"contentHash": "X7wSSBNFRuN8j8M9HDYG7rPpEeyhY+PdJZR9rftmgvsZH0eK5+bZ3b3As8iO4rLEpjsBzDnrgSIY6q2F3HQatw=="
},
"Microsoft.EntityFrameworkCore.Analyzers": {
"type": "Transitive",
"resolved": "8.0.6",
"contentHash": "fDNtuQ4lAaPaCOlsrwUck/GvnF4QLeDpMmE1L5QtxZpMSmWfnL2/vk8sDL9OVTWcfprooI9V5MNpIx3/Tq5ehg=="
},
"Microsoft.EntityFrameworkCore.Relational": {
"type": "Transitive",
"resolved": "8.0.6",
"contentHash": "chhfmLusCGLGvNYtvMji6KGQlduPDnJsStG/LjS8qJhFWJDDzTZpSr2LHowewcxMrMo/Axc6Jwe+WwSi/vlkTg==",
"dependencies": {
"Microsoft.EntityFrameworkCore": "8.0.6",
"Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
}
},
"Microsoft.EntityFrameworkCore.Sqlite.Core": {
"type": "Transitive",
"resolved": "8.0.6",
"contentHash": "87xfPtqSouxWWdynYZv/rubd0rOUeiN9+XeoMWQzpZm/5svH1TuvzFODGIY0zKuXS18NiOFyHl9N6///eaEs/Q==",
"dependencies": {
"Microsoft.Data.Sqlite.Core": "8.0.6",
"Microsoft.EntityFrameworkCore.Relational": "8.0.6",
"Microsoft.Extensions.DependencyModel": "8.0.0"
}
},
"Microsoft.Extensions.Caching.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==",
"dependencies": {
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Caching.Memory": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "7pqivmrZDzo1ADPkRwjy+8jtRKWRCPag9qPI+p7sgu7Q4QreWhcvbiWXsbhP+yY8XSiDvZpu2/LWdBv7PnmOpQ==",
"dependencies": {
"Microsoft.Extensions.Caching.Abstractions": "8.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
"Microsoft.Extensions.Options": "8.0.0",
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
"dependencies": {
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg=="
},
"Microsoft.Extensions.DependencyModel": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "NSmDw3K0ozNDgShSIpsZcbFIzBX4w28nDag+TfaQujkXGazBm+lid5onlWoCBy4VsLxqnnKjEBbGSJVWJMf43g==",
"dependencies": {
"System.Text.Encodings.Web": "8.0.0",
"System.Text.Json": "8.0.0"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
}
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
"Microsoft.Extensions.Primitives": "8.0.0"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
},
"Microsoft.SourceLink.AzureRepos.Git": {
"type": "Transitive",
"resolved": "1.1.1",
"contentHash": "qB5urvw9LO2bG3eVAkuL+2ughxz2rR7aYgm2iyrB8Rlk9cp2ndvGRCvehk3rNIhRuNtQaeKwctOl1KvWiklv5w==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "1.1.1",
"Microsoft.SourceLink.Common": "1.1.1"
}
},
"Microsoft.SourceLink.Bitbucket.Git": {
"type": "Transitive",
"resolved": "1.1.1",
"contentHash": "cDzxXwlyWpLWaH0em4Idj0H3AmVo3L/6xRXKssYemx+7W52iNskj/SQ4FOmfCb8YQt39otTDNMveCZzYtMoucQ==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "1.1.1",
"Microsoft.SourceLink.Common": "1.1.1"
}
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
},
"Microsoft.SourceLink.GitHub": {
"type": "Transitive",
"resolved": "1.1.1",
"contentHash": "IaJGnOv/M7UQjRJks7B6p7pbPnOwisYGOIzqCz5ilGFTApZ3ktOR+6zJ12ZRPInulBmdAf1SrGdDG2MU8g6XTw==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "1.1.1",
"Microsoft.SourceLink.Common": "1.1.1"
}
},
"Microsoft.SourceLink.GitLab": {
"type": "Transitive",
"resolved": "1.1.1",
"contentHash": "tvsg47DDLqqedlPeYVE2lmiTpND8F0hkrealQ5hYltSmvruy/Gr5nHAKSsjyw5L3NeM/HLMI5ORv7on/M4qyZw==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "1.1.1",
"Microsoft.SourceLink.Common": "1.1.1"
}
},
"SQLitePCLRaw.bundle_e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.6",
"contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==",
"dependencies": {
"SQLitePCLRaw.lib.e_sqlite3": "2.1.6",
"SQLitePCLRaw.provider.e_sqlite3": "2.1.6"
}
},
"SQLitePCLRaw.core": {
"type": "Transitive",
"resolved": "2.1.6",
"contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==",
"dependencies": {
"System.Memory": "4.5.3"
}
},
"SQLitePCLRaw.lib.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.6",
"contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q=="
},
"SQLitePCLRaw.provider.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.6",
"contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==",
"dependencies": {
"SQLitePCLRaw.core": "2.1.6"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.3",
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
},
"System.Text.Json": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "OdrZO2WjkiEG6ajEFRABTRCi/wuXQPxeV6g8xvUJqdxMvvuCCEk86zPla8UiIQJz3durtUEbNyY/3lIhS0yZvQ==",
"dependencies": {
"System.Text.Encodings.Web": "8.0.0"
}
},
"ecommons": {
"type": "Project"
},
"llib": {
"type": "Project",
"dependencies": {
"DalamudPackager": "[2.1.13, )"
}
},
"pal.common": {
"type": "Project"
}
},
"net8.0-windows7.0/win-x64": {
"SQLitePCLRaw.lib.e_sqlite3": {
"type": "Transitive",
"resolved": "2.1.6",
"contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q=="
},
"System.Text.Encodings.Web": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
}
}
}
}

View File

@ -1,12 +1,12 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace Pal.Common namespace Pal.Common;
[SuppressMessage("ReSharper", "UnusedMember.Global")]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum ETerritoryType : ushort
{ {
[SuppressMessage("ReSharper", "UnusedMember.Global")]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum ETerritoryType : ushort
{
Palace_1_10 = 561, Palace_1_10 = 561,
Palace_11_20, Palace_11_20,
Palace_21_30, Palace_21_30,
@ -47,6 +47,16 @@ namespace Pal.Common
[Display(Order = 9)] [Display(Order = 9)]
HeavenOnHigh_81_90 = 775, HeavenOnHigh_81_90 = 775,
[Display(Order = 10)] [Display(Order = 10)]
HeavenOnHigh_91_100 = 785 HeavenOnHigh_91_100 = 785,
}
EurekaOrthos_1_10 = 1099,
EurekaOrthos_11_20,
EurekaOrthos_21_30,
EurekaOrthos_31_40,
EurekaOrthos_41_50,
EurekaOrthos_51_60,
EurekaOrthos_61_70,
EurekaOrthos_71_80,
EurekaOrthos_81_90,
EurekaOrthos_91_100
} }

View File

@ -1,10 +1,12 @@
using System.ComponentModel.DataAnnotations; using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection; using System.Reflection;
namespace Pal.Common namespace Pal.Common;
public static class EnumExtensions
{ {
public static class EnumExtensions
{
public static int? GetOrder(this Enum e) public static int? GetOrder(this Enum e)
{ {
Type type = e.GetType(); Type type = e.GetType();
@ -12,5 +14,4 @@ namespace Pal.Common
DisplayAttribute? attribute = field.GetCustomAttributes(typeof(DisplayAttribute), false).Cast<DisplayAttribute>().FirstOrDefault(); DisplayAttribute? attribute = field.GetCustomAttributes(typeof(DisplayAttribute), false).Cast<DisplayAttribute>().FirstOrDefault();
return attribute?.Order; return attribute?.Order;
} }
}
} }

View File

@ -4,10 +4,9 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Pal.Common namespace Pal.Common;
public static class ExportConfig
{ {
public static class ExportConfig
{
public static int ExportVersion => 2; public static int ExportVersion => 2;
}
} }

View File

@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<LangVersion>11.0</LangVersion> <LangVersion>12.0</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<DebugType>portable</DebugType> <DebugType>portable</DebugType>
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap> <PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -1,9 +1,10 @@
using System.Numerics; using System;
using System.Numerics;
namespace Pal.Common namespace Pal.Common;
public class PalaceMath
{ {
public class PalaceMath
{
private static readonly Vector3 ScaleFactor = new(5); private static readonly Vector3 ScaleFactor = new(5);
public static bool IsNearlySamePosition(Vector3 a, Vector3 b) public static bool IsNearlySamePosition(Vector3 a, Vector3 b)
@ -18,5 +19,4 @@ namespace Pal.Common
v *= ScaleFactor; v *= ScaleFactor;
return HashCode.Combine((int)v.X, (int)v.Y, (int)v.Z); return HashCode.Combine((int)v.X, (int)v.Y, (int)v.Z);
} }
}
} }

View File

@ -18,6 +18,7 @@ service AccountService {
} }
message CreateAccountRequest { message CreateAccountRequest {
Version version = 1;
} }
message CreateAccountReply { message CreateAccountReply {
@ -35,6 +36,7 @@ enum CreateAccountError {
message LoginRequest { message LoginRequest {
string accountId = 1; string accountId = 1;
Version version = 2;
} }
message LoginReply { message LoginReply {
@ -56,3 +58,8 @@ message VerifyRequest {
message VerifyReply { message VerifyReply {
} }
message Version {
int32 major = 1;
int32 minor = 2;
}

View File

@ -1,6 +1,6 @@
syntax = "proto3"; syntax = "proto3";
package account; package export;
import "google/protobuf/timestamp.proto"; import "google/protobuf/timestamp.proto";

View File

@ -0,0 +1,6 @@
{
"version": 1,
"dependencies": {
"net8.0": {}
}
}

23
Pal.sln
View File

@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.3.32929.385 VisualStudioVersion = 17.3.32929.385
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pal.Server", "Pal.Server\Pal.Server.csproj", "{AB3E2849-DB06-46F6-8457-9AC1096B4125}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pal.Server", "Server\Server\Pal.Server.csproj", "{AB3E2849-DB06-46F6-8457-9AC1096B4125}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pal.Client", "Pal.Client\Pal.Client.csproj", "{7F1985B3-D376-4A91-BC9B-46C2A860F9EF}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pal.Client", "Pal.Client\Pal.Client.csproj", "{7F1985B3-D376-4A91-BC9B-46C2A860F9EF}"
EndProject EndProject
@ -13,6 +13,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
Dockerfile = Dockerfile Dockerfile = Dockerfile
README.md = README.md README.md = README.md
.editorconfig = .editorconfig
EndProjectSection EndProjectSection
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ECommons", "vendor\ECommons\ECommons\ECommons.csproj", "{D0B37096-5BC3-41B0-8D81-203CBA3932B0}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ECommons", "vendor\ECommons\ECommons\ECommons.csproj", "{D0B37096-5BC3-41B0-8D81-203CBA3932B0}"
@ -24,6 +25,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "github-workflows", "github-
.github\workflows\upload-crowdin.yml = .github\workflows\upload-crowdin.yml .github\workflows\upload-crowdin.yml = .github\workflows\upload-crowdin.yml
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pal.Server.Tests", "Server\Tests\Pal.Server.Tests.csproj", "{AEC052FA-F178-492C-9A09-ED28DBE1EF5E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LLib", "vendor\LLib\LLib.csproj", "{B1321FD5-7BBF-4C9D-83C1-F8D7C394F32A}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -64,6 +69,22 @@ Global
{D0B37096-5BC3-41B0-8D81-203CBA3932B0}.Release|Any CPU.Build.0 = Release|x64 {D0B37096-5BC3-41B0-8D81-203CBA3932B0}.Release|Any CPU.Build.0 = Release|x64
{D0B37096-5BC3-41B0-8D81-203CBA3932B0}.Release|x64.ActiveCfg = Release|x64 {D0B37096-5BC3-41B0-8D81-203CBA3932B0}.Release|x64.ActiveCfg = Release|x64
{D0B37096-5BC3-41B0-8D81-203CBA3932B0}.Release|x64.Build.0 = Release|x64 {D0B37096-5BC3-41B0-8D81-203CBA3932B0}.Release|x64.Build.0 = Release|x64
{AEC052FA-F178-492C-9A09-ED28DBE1EF5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AEC052FA-F178-492C-9A09-ED28DBE1EF5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AEC052FA-F178-492C-9A09-ED28DBE1EF5E}.Debug|x64.ActiveCfg = Debug|Any CPU
{AEC052FA-F178-492C-9A09-ED28DBE1EF5E}.Debug|x64.Build.0 = Debug|Any CPU
{AEC052FA-F178-492C-9A09-ED28DBE1EF5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AEC052FA-F178-492C-9A09-ED28DBE1EF5E}.Release|Any CPU.Build.0 = Release|Any CPU
{AEC052FA-F178-492C-9A09-ED28DBE1EF5E}.Release|x64.ActiveCfg = Release|Any CPU
{AEC052FA-F178-492C-9A09-ED28DBE1EF5E}.Release|x64.Build.0 = Release|Any CPU
{B1321FD5-7BBF-4C9D-83C1-F8D7C394F32A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B1321FD5-7BBF-4C9D-83C1-F8D7C394F32A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B1321FD5-7BBF-4C9D-83C1-F8D7C394F32A}.Debug|x64.ActiveCfg = Debug|Any CPU
{B1321FD5-7BBF-4C9D-83C1-F8D7C394F32A}.Debug|x64.Build.0 = Debug|Any CPU
{B1321FD5-7BBF-4C9D-83C1-F8D7C394F32A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B1321FD5-7BBF-4C9D-83C1-F8D7C394F32A}.Release|Any CPU.Build.0 = Release|Any CPU
{B1321FD5-7BBF-4C9D-83C1-F8D7C394F32A}.Release|x64.ActiveCfg = Release|Any CPU
{B1321FD5-7BBF-4C9D-83C1-F8D7C394F32A}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -5,15 +5,14 @@ Shows possible trap & hoard coffer locations in Palace of the Dead & Heaven on H
## Installation ## Installation
To install this plugin from my plugin repository, please check the To install this plugin from my plugin repository, please check the
[Installation Instructions](https://github.com/carvelli/Dalamud-Plugins#installation). [Installation Instructions](https://git.carvel.li/liza/plugin-repo/src/branch/master/README.md).
Additionally, you **need to install Splatoon**, which is used to render the visible overlays. Additionally, you **need to install Splatoon**, which is used to render the visible overlays.
Please check [Splatoon's Installation Instructions](https://github.com/NightmareXIV/MyDalamudPlugins#installation). Please check [Splatoon's Installation Instructions](https://github.com/NightmareXIV/MyDalamudPlugins#installation).
## Translation ## Translation
Please feel free to help by [translating this plugin into your language](https://crowdin.com/project/palace-pal). Please feel free to help by [translating this plugin into your language](https://crowdin.com/project/palace-pal).
If you want to translate the plugin into a language that is currently not enabled, If you want to translate the plugin into a language that is currently not enabled,
[please create a new issue](https://github.com/carvelli/PalacePal/issues/new). [please create a new issue](https://git.carvel.li/liza/PalacePal/issues/new).

1
Server Submodule

@ -0,0 +1 @@
Subproject commit e59583fac353fdd960556ed2fa65220d66db5637

Some files were not shown because too many files have changed in this diff Show More