Compare commits

...

60 Commits
v3.3 ... master

Author SHA1 Message Date
d239a6e4b0
API 10 2024-07-06 13:26:39 +02:00
9d5e4a797b
Update Dockerfile 2024-07-06 13:20:17 +02:00
3e2f917c14
API 10 2024-07-06 13:14:59 +02:00
2fdf9b1e4d
Remove GitInfo dependency 2024-01-16 11:41:13 +01:00
18ffd66086
Update icon URL 2024-01-16 07:03:56 +01:00
64e576a004
Render: Ensure RenderElement is null if we don't recreate it 2023-11-30 14:01:59 +01:00
0d1882d97f
Render: Performance fix 2023-11-26 22:14:04 +01:00
964119cfd2
Render: Use enabled instead of color for showing/hiding elements 2023-11-26 20:13:17 +01:00
309edfcd17
Bump version/recompile 2023-11-14 20:21:40 +01:00
8fbd3fbc0d
Update docker build 2023-11-11 01:56:09 +01:00
ce9be45e8f
Update docker build 2023-11-11 01:54:43 +01:00
8def5e28a4
Update dependencies 2023-11-11 01:41:15 +01:00
ba06576c2d
Update dependencies 2023-11-11 01:39:17 +01:00
f259f03534
Update server submodule structure 2023-11-11 01:39:10 +01:00
cf3a24afe8
Update header icon logic for Dalamud changes 2023-11-09 11:32:32 +01:00
2a180345eb
Update submodule URL 2023-10-14 00:02:21 +02:00
345c9c59a3
Add LLib, remove outdated info, add config window constraints 2023-10-11 03:56:18 +02:00
5c4e9f30e0
API 9 2023-10-03 22:05:19 +02:00
d337413b82 Update docker-build.sh 2023-10-03 17:54:53 +00:00
5a5d7fecfd
Change nuget lib path 2023-10-03 11:10:43 +02:00
1ee86d378f
API 9 2023-10-03 11:08:38 +02:00
cd52192d15
Setup server/test submodules 2023-09-17 22:28:37 +02:00
6260f35b61 Update repo location 2023-07-31 02:58:24 +02:00
79ad7fbc39 Update README 2023-07-30 23:53:47 +02:00
1bdcc7179c Update image location 2023-07-30 23:26:15 +02:00
5ba18477b4 Update repo url 2023-07-30 23:22:33 +02:00
5f506bda8a Update server url 2023-07-30 22:32:39 +02:00
bbec57c3ad Update actions 2023-07-30 21:49:14 +02:00
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
8042aee951 Use file-scoped namespaces 2023-03-30 22:02:51 +02:00
0b07e8b8f6 Code cleanup 2023-03-27 20:00:30 +02:00
7e73430179 Add palace service tests, remove standalone client 2023-03-27 18:02:18 +02:00
cec3597629 Update dependencies 2023-03-26 15:57:50 +02:00
ae29eaa52f Update ECommons 2023-03-26 15:49:52 +02:00
b81ced33e8 Code style tweaks 2023-03-26 15:47:43 +02:00
87806397d1 Configure GHA caching 2023-03-26 14:26:12 +02:00
d645aa9ea7 Configure GHA dotnet test timeout 2023-03-26 13:55:30 +02:00
4288440e6c Add export tests 2023-03-26 03:11:01 +02:00
36b328c29e Add simple account test 2023-03-25 19:14:32 +01:00
232ab164be Update DE localization 2023-03-25 02:47:21 +01:00
a4890c0159 Fix duplicate seen location errors 2023-03-25 02:39:54 +01:00
7bdf97411c Hide occasional FileLoadException (temporary error) 2023-03-11 01:50:26 +01:00
3e9f14419c Update gRPC packages 2023-03-10 03:08:44 +01:00
a21e9335aa GHA: Add workflow_dispatch to docker build 2023-03-10 02:44:04 +01:00
1b415d7a7f Improve repo exception handling 2023-03-08 01:27:10 +01:00
c5acb2ca54 Eureka Orthos: Add owlet trap to pomander of sight 2023-03-07 23:28:58 +01:00
7a514fad2a Add debug feature for object table 2023-03-07 21:55:50 +01:00
125b687a9c Eureka Orthos: Territory types 2023-03-07 12:58:10 +01:00
07ed62cd9a Update JP & DE localization 2023-03-02 02:00:05 +01:00
7179d41e59 Fix local seen 2023-02-26 17:54:15 +01:00
8279bfe9bf Fix config changes to trap/coffer locations not being applied when saving 2023-02-26 17:43:20 +01:00
e45b72a655 Add gold coffers (#10) 2023-02-26 17:31:37 +01:00
cbdcf58063 Move logging into nuget package 2023-02-25 15:32:26 +01:00
d25e21619d Merge branch 'signtool-fix' 2023-02-25 01:35:26 +01:00
2bfbeacdd0 fix CI build 2023-02-25 01:32:39 +01:00
88ec7a00da Improve error handling if a command fails with an exception 2023-02-25 01:15:19 +01:00
0c922c1695 Use PalConfigCommand for the config UI button (partial revert of previous commit) 2023-02-25 01:07:10 +01:00
e1dc8cafd9 Fix /pal test-connection not working if using Simple renderer 2023-02-25 01:06:33 +01:00
e79e8de6dc Use ISubCommand for subcommands 2023-02-25 00:55:48 +01:00
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
Dockerfile
.dockerignore
docker-build.sh
.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"]
path = vendor/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
COPY Pal.Common/Pal.Common.csproj Pal.Common/
COPY Pal.Server/Pal.Server.csproj Pal.Server/
RUN dotnet restore Pal.Server/Pal.Server.csproj
COPY Server/Server/Pal.Server.csproj Server/Server/
RUN dotnet restore Server/Server/Pal.Server.csproj -a $TARGETARCH
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
ENV DOTNET_ENVIRONMENT=Production
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,10 +1,11 @@
using Dalamud.Interface.Windowing;
using System;
using System.Collections.Generic;
using Pal.Client.Configuration;
using Pal.Client.Windows;
namespace Pal.Client.Commands
{
internal class PalConfigCommand
namespace Pal.Client.Commands;
internal class PalConfigCommand : ISubCommand
{
private readonly IPalacePalConfiguration _configuration;
private readonly AgreementWindow _agreementWindow;
@ -20,6 +21,14 @@ namespace Pal.Client.Commands
_configWindow = configWindow;
}
public IReadOnlyDictionary<string, Action<string>> GetHandlers()
=> new Dictionary<string, Action<string>>
{
{ "config", _ => Execute() },
{ "", _ => Execute() }
};
public void Execute()
{
if (_configuration.FirstUse)
@ -28,4 +37,3 @@ namespace Pal.Client.Commands
_configWindow.Toggle();
}
}
}

View File

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

View File

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

View File

@ -1,9 +1,11 @@
using ECommons.Schedulers;
using System;
using System.Collections.Generic;
using ECommons.Schedulers;
using Pal.Client.Windows;
namespace Pal.Client.Commands
{
internal sealed class PalTestConnectionCommand
namespace Pal.Client.Commands;
internal sealed class PalTestConnectionCommand : ISubCommand
{
private readonly ConfigWindow _configWindow;
@ -12,9 +14,16 @@ namespace Pal.Client.Commands
_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());
}
}
}

View File

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

View File

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

View File

@ -13,17 +13,17 @@ using Pal.Client.Configuration.Legacy;
using Pal.Client.Database;
using NJson = Newtonsoft.Json;
namespace Pal.Client.Configuration
{
namespace Pal.Client.Configuration;
internal sealed class ConfigurationManager
{
private readonly ILogger<ConfigurationManager> _logger;
private readonly DalamudPluginInterface _pluginInterface;
private readonly IDalamudPluginInterface _pluginInterface;
private readonly IServiceProvider _serviceProvider;
public event EventHandler<IPalacePalConfiguration>? Saved;
public ConfigurationManager(ILogger<ConfigurationManager> logger, DalamudPluginInterface pluginInterface,
public ConfigurationManager(ILogger<ConfigurationManager> logger, IDalamudPluginInterface pluginInterface,
IServiceProvider serviceProvider)
{
_logger = logger;
@ -87,7 +87,9 @@ namespace Pal.Client.Configuration
dbContext.Imports.Add(new ImportHistory
{
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,
ImportedAt = importHistory.ImportedAt
});
@ -98,6 +100,17 @@ namespace Pal.Client.Configuration
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)
@ -141,7 +154,9 @@ namespace Pal.Client.Configuration
if (string.IsNullOrEmpty(accountId))
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);
newAccount.CachedRoles = oldAccount.CachedRoles.ToList();
}
@ -153,4 +168,3 @@ namespace Pal.Client.Configuration
#pragma warning restore CS0618
#pragma warning restore CS0612
}
}

View File

@ -2,8 +2,8 @@
using System.Collections.Generic;
using System.Linq;
namespace Pal.Client.Configuration
{
namespace Pal.Client.Configuration;
public sealed class ConfigurationV7 : IPalacePalConfiguration, IConfigurationInConfigDirectory
{
public int Version { get; set; } = 7;
@ -51,4 +51,3 @@ namespace Pal.Client.Configuration
return account == null || account.CachedRoles.Contains(role);
}
}
}

View File

@ -1,5 +1,5 @@
namespace Pal.Client.Configuration
{
namespace Pal.Client.Configuration;
public enum EMode
{
/// <summary>
@ -8,8 +8,7 @@
Online = 1,
/// <summary>
/// Only shows traps found by yourself uisng a pomander of sight.
/// Only shows traps found by yourself using a pomander of sight.
/// </summary>
Offline = 2,
}
}

View File

@ -1,5 +1,5 @@
namespace Pal.Client.Configuration
{
namespace Pal.Client.Configuration;
public enum ERenderer
{
/// <see cref="Rendering.SimpleRenderer"/>
@ -8,4 +8,3 @@
/// <see cref="Rendering.SplatoonRenderer"/>
Splatoon = 1,
}
}

View File

@ -4,8 +4,8 @@ using System.Numerics;
using ImGuiNET;
using Newtonsoft.Json;
namespace Pal.Client.Configuration
{
namespace Pal.Client.Configuration;
public interface IVersioned
{
int Version { get; set; }
@ -19,6 +19,7 @@ namespace Pal.Client.Configuration
bool FirstUse { get; set; }
EMode Mode { get; set; }
string BetaKey { get; }
bool HasBetaFeature(string feature) => BetaKey.Contains(feature);
DeepDungeonConfiguration DeepDungeons { get; set; }
RendererConfiguration Renderer { get; set; }
@ -56,6 +57,14 @@ namespace Pal.Client.Configuration
OnlyVisibleAfterPomander = false,
Fill = true
};
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
@ -99,4 +108,3 @@ namespace Pal.Client.Configuration
public int MinimumBackupsToKeep { get; set; } = 3;
public int DaysToDeleteAfter { get; set; } = 21;
}
}

View File

@ -8,8 +8,8 @@ using Dalamud.Plugin;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace Pal.Client.Configuration.Legacy
{
namespace Pal.Client.Configuration.Legacy;
[Obsolete]
public sealed class ConfigurationV1
{
@ -50,7 +50,7 @@ namespace Pal.Client.Configuration.Legacy
public string BetaKey { get; set; } = "";
#endregion
public void Migrate(DalamudPluginInterface pluginInterface, ILogger<ConfigurationV1> logger)
public void Migrate(IDalamudPluginInterface pluginInterface, ILogger<ConfigurationV1> logger)
{
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
{
@ -164,4 +164,3 @@ namespace Pal.Client.Configuration.Legacy
public DateTime ImportedAt { get; set; }
}
}
}

View File

@ -7,8 +7,8 @@ using System.Text.Json;
using Pal.Client.Extensions;
using Pal.Common;
namespace Pal.Client.Configuration.Legacy
{
namespace Pal.Client.Configuration.Legacy;
/// <summary>
/// Legacy JSON file for marker locations.
/// </summary>
@ -19,7 +19,6 @@ namespace Pal.Client.Configuration.Legacy
private const int CurrentVersion = 4;
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)
{
@ -36,11 +35,6 @@ namespace Pal.Client.Configuration.Legacy
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));
}
@ -165,4 +159,3 @@ namespace Pal.Client.Configuration.Legacy
public HashSet<JsonMarker> Markers { get; set; } = new();
}
}
}

View File

@ -2,8 +2,8 @@
using System.Collections.Generic;
using System.Numerics;
namespace Pal.Client.Configuration.Legacy
{
namespace Pal.Client.Configuration.Legacy;
[Obsolete]
public class JsonMarker
{
@ -23,4 +23,3 @@ namespace Pal.Client.Configuration.Legacy
Debug = 3,
}
}
}

View File

@ -12,8 +12,8 @@ using Microsoft.Extensions.Logging;
using Pal.Client.Database;
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>
@ -21,10 +21,10 @@ namespace Pal.Client.Configuration.Legacy
{
private readonly ILogger<JsonMigration> _logger;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly DalamudPluginInterface _pluginInterface;
private readonly IDalamudPluginInterface _pluginInterface;
public JsonMigration(ILogger<JsonMigration> logger, IServiceScopeFactory serviceScopeFactory,
DalamudPluginInterface pluginInterface)
IDalamudPluginInterface pluginInterface)
{
_logger = logger;
_serviceScopeFactory = serviceScopeFactory;
@ -144,4 +144,3 @@ namespace Pal.Client.Configuration.Legacy
}
#pragma warning restore CS0612
}
}

View File

@ -6,8 +6,8 @@ using Microsoft.Extensions.Logging;
using Pal.Client.Configuration;
using Pal.Common;
namespace Pal.Client.Database
{
namespace Pal.Client.Database;
internal sealed class Cleanup
{
private readonly ILogger<Cleanup> _logger;
@ -64,4 +64,3 @@ namespace Pal.Client.Database
return o => o.Source != ClientLocation.ESource.Download;
}
}
}

View File

@ -1,8 +1,8 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Pal.Client.Database
{
namespace Pal.Client.Database;
internal sealed class ClientLocation
{
[Key] public int LocalId { get; set; }
@ -56,4 +56,3 @@ namespace Pal.Client.Database
Download = 4,
}
}
}

View File

@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
namespace Pal.Client.Database
{
namespace Pal.Client.Database;
internal sealed class ImportHistory
{
public Guid Id { get; set; }
@ -12,4 +12,3 @@ namespace Pal.Client.Database
public List<ClientLocation> ImportedLocations { get; set; } = new();
}
}

View File

@ -1,7 +1,7 @@
using Microsoft.EntityFrameworkCore;
namespace Pal.Client.Database
{
namespace Pal.Client.Database;
internal class PalClientContext : DbContext
{
public DbSet<ClientLocation> Locations { get; set; } = null!;
@ -21,4 +21,3 @@ namespace Pal.Client.Database
.UsingEntity(o => o.ToTable("LocationImports"));
}
}
}

View File

@ -2,8 +2,8 @@
using Pal.Client.Extensions;
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
@ -38,4 +38,3 @@ namespace Pal.Client.Database
AccountId = accountId.ToPartialId();
}
}
}

View File

@ -19,8 +19,8 @@ using Pal.Client.DependencyInjection;
using Pal.Client.Floors;
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.
@ -69,10 +69,12 @@ namespace Pal.Client
_serviceProvider.GetRequiredService<ChatService>();
// eager load any commands to find errors now, not when running them
_serviceProvider.GetRequiredService<PalConfigCommand>();
_serviceProvider.GetRequiredService<PalNearCommand>();
_serviceProvider.GetRequiredService<PalStatsCommand>();
_serviceProvider.GetRequiredService<PalTestConnectionCommand>();
_serviceProvider.GetRequiredService<IEnumerable<ISubCommand>>();
cancellationToken.ThrowIfCancellationRequested();
if (_serviceProvider.GetRequiredService<IPalacePalConfiguration>().HasBetaFeature(ObjectTableDebug.FeatureName))
_serviceProvider.GetRequiredService<ObjectTableDebug>();
cancellationToken.ThrowIfCancellationRequested();
@ -82,7 +84,7 @@ namespace Pal.Client
private async Task RemoveOldBackups()
{
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 paths = Directory.GetFiles(pluginInterface.GetPluginConfigDirectory(), "backup-*.data.sqlite3",
@ -134,7 +136,7 @@ namespace Pal.Client
{
await using var scope = _serviceProvider.CreateAsyncScope();
var pluginInterface = scope.ServiceProvider.GetRequiredService<DalamudPluginInterface>();
var pluginInterface = scope.ServiceProvider.GetRequiredService<IDalamudPluginInterface>();
string backupPath = Path.Join(pluginInterface.GetPluginConfigDirectory(),
$"backup-{DateTime.Now.ToUniversalTime():yyyy-MM-dd}.data.sqlite3");
string sourcePath = Path.Join(pluginInterface.GetPluginConfigDirectory(),
@ -191,4 +193,3 @@ namespace Pal.Client
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.Plugin.Services;
using ECommons.DalamudServices.Legacy;
using Pal.Client.Properties;
namespace Pal.Client.DependencyInjection
{
namespace Pal.Client.DependencyInjection;
internal sealed class Chat
{
private readonly ChatGui _chatGui;
private readonly IChatGui _chatGui;
public Chat(ChatGui chatGui)
public Chat(IChatGui chatGui)
{
_chatGui = chatGui;
}
@ -35,4 +36,3 @@ namespace Pal.Client.DependencyInjection
public void UnformattedMessage(string message)
=> _chatGui.Print(message);
}
}

View File

@ -1,25 +1,25 @@
using System;
using System.Text;
using System.Text.RegularExpressions;
using Dalamud.Data;
using Dalamud.Game.Gui;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Plugin.Services;
using Lumina.Excel.GeneratedSheets;
using Pal.Client.Configuration;
using Pal.Client.Floors;
namespace Pal.Client.DependencyInjection
{
namespace Pal.Client.DependencyInjection;
internal sealed class ChatService : IDisposable
{
private readonly ChatGui _chatGui;
private readonly IChatGui _chatGui;
private readonly TerritoryState _territoryState;
private readonly IPalacePalConfiguration _configuration;
private readonly DataManager _dataManager;
private readonly IDataManager _dataManager;
private readonly LocalizedChatMessages _localizedChatMessages;
public ChatService(ChatGui chatGui, TerritoryState territoryState, IPalacePalConfiguration configuration,
DataManager dataManager)
public ChatService(IChatGui chatGui, TerritoryState territoryState, IPalacePalConfiguration configuration,
IDataManager dataManager)
{
_chatGui = chatGui;
_territoryState = territoryState;
@ -34,7 +34,7 @@ namespace Pal.Client.DependencyInjection
public void Dispose()
=> _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)
{
if (_configuration.FirstUse)
@ -82,14 +82,21 @@ namespace Pal.Client.DependencyInjection
HoardNotOnCurrentFloor = GetLocalizedString(7273),
HoardCofferOpened = GetLocalizedString(7274),
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
@ -107,4 +114,3 @@ namespace Pal.Client.DependencyInjection
new(@"This isn't a game message, but will be replaced"); // new Regex(@"^Floor (\d+)$");
}
}
}

View File

@ -1,7 +1,7 @@
using System;
namespace Pal.Client.DependencyInjection
{
namespace Pal.Client.DependencyInjection;
internal sealed class DebugState
{
public string? DebugMessage { get; set; }
@ -12,4 +12,3 @@ namespace Pal.Client.DependencyInjection
public void Reset()
=> DebugMessage = null;
}
}

View File

@ -4,16 +4,17 @@ using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Hooking;
using Dalamud.Memory;
using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using Microsoft.Extensions.Logging;
using Pal.Client.Floors;
namespace Pal.Client.DependencyInjection
{
namespace Pal.Client.DependencyInjection;
internal sealed unsafe class GameHooks : IDisposable
{
private readonly ILogger<GameHooks> _logger;
private readonly ObjectTable _objectTable;
private readonly IObjectTable _objectTable;
private readonly TerritoryState _territoryState;
private readonly FrameworkService _frameworkService;
@ -24,7 +25,7 @@ namespace Pal.Client.DependencyInjection
private Hook<ActorVfxCreateDelegate> ActorVfxCreateHook { get; init; } = null!;
#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;
_objectTable = objectTable;
@ -32,7 +33,7 @@ namespace Pal.Client.DependencyInjection
_frameworkService = frameworkService;
_logger.LogDebug("Initializing game hooks");
SignatureHelper.Initialise(this);
gameInteropProvider.InitializeFromAttributes(this);
ActorVfxCreateHook.Enable();
_logger.LogDebug("Game hooks initialized");
@ -80,7 +81,7 @@ namespace Pal.Client.DependencyInjection
_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")
{
@ -103,4 +104,3 @@ namespace Pal.Client.DependencyInjection
ActorVfxCreateHook.Dispose();
}
}
}

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
using Account;
using Export;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Pal.Client.Database;
@ -12,8 +12,8 @@ using Pal.Client.Floors;
using Pal.Client.Floors.Tasks;
using Pal.Common;
namespace Pal.Client.DependencyInjection
{
namespace Pal.Client.DependencyInjection;
internal sealed class ImportService
{
private readonly IServiceProvider _serviceProvider;
@ -163,4 +163,3 @@ namespace Pal.Client.DependencyInjection
}
}
}
}

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

View File

@ -2,25 +2,32 @@
using System.Threading.Tasks;
using Dalamud.Game.Gui;
using Grpc.Core;
using Microsoft.Extensions.Logging;
using Pal.Client.Configuration;
using Pal.Client.Extensions;
using Pal.Client.Net;
using Pal.Client.Properties;
using Pal.Client.Windows;
namespace Pal.Client.DependencyInjection
{
namespace Pal.Client.DependencyInjection;
internal sealed class StatisticsService
{
private readonly IPalacePalConfiguration _configuration;
private readonly ILogger<StatisticsService> _logger;
private readonly RemoteApi _remoteApi;
private readonly StatisticsWindow _statisticsWindow;
private readonly Chat _chat;
public StatisticsService(IPalacePalConfiguration configuration, RemoteApi remoteApi,
StatisticsWindow statisticsWindow, Chat chat)
public StatisticsService(
IPalacePalConfiguration configuration,
ILogger<StatisticsService> logger,
RemoteApi remoteApi,
StatisticsWindow statisticsWindow,
Chat chat)
{
_configuration = configuration;
_logger = logger;
_remoteApi = remoteApi;
_statisticsWindow = statisticsWindow;
_chat = chat;
@ -54,12 +61,14 @@ namespace Pal.Client.DependencyInjection
}
catch (RpcException e) when (e.StatusCode == StatusCode.PermissionDenied)
{
_logger.LogWarning(e, "Access denied while fetching floor statistics");
_chat.Error(Localization.Command_pal_stats_CurrentFloor);
}
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.IO;
using Dalamud.Data;
using Dalamud.Extensions.MicrosoftLogging;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Conditions;
@ -9,6 +10,7 @@ using Dalamud.Game.Command;
using Dalamud.Game.Gui;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
@ -18,47 +20,46 @@ using Pal.Client.Configuration;
using Pal.Client.Configuration.Legacy;
using Pal.Client.Database;
using Pal.Client.DependencyInjection;
using Pal.Client.DependencyInjection.Logging;
using Pal.Client.Floors;
using Pal.Client.Net;
using Pal.Client.Properties;
using Pal.Client.Rendering;
using Pal.Client.Scheduled;
using Pal.Client.Windows;
namespace Pal.Client
{
namespace Pal.Client;
/// <summary>
/// DI-aware Plugin.
/// </summary>
internal sealed class DependencyInjectionContext : IDisposable
{
public const string DatabaseFileName = "palace-pal.data.sqlite3";
public static DalamudLoggerProvider LoggerProvider { get; } = new();
/// <summary>
/// Initialized as temporary logger, will be overriden once context is ready with a logger that supports scopes.
/// </summary>
private ILogger _logger = LoggerProvider.CreateLogger<DependencyInjectionContext>();
private ILogger _logger;
private readonly string _sqliteConnectionString;
private readonly ServiceCollection _serviceCollection = new();
private ServiceProvider? _serviceProvider;
public string Name => Localization.Palace_Pal;
public DependencyInjectionContext(
DalamudPluginInterface pluginInterface,
ClientState clientState,
GameGui gameGui,
ChatGui chatGui,
ObjectTable objectTable,
Framework framework,
Condition condition,
CommandManager commandManager,
DataManager dataManager,
IDalamudPluginInterface pluginInterface,
IClientState clientState,
IGameGui gameGui,
IChatGui chatGui,
IObjectTable objectTable,
IFramework framework,
ICondition condition,
ICommandManager commandManager,
IDataManager dataManager,
IGameInteropProvider gameInteropProvider,
IPluginLog pluginLog,
Plugin plugin)
{
var loggerProvider = new DalamudLoggerProvider(pluginLog);
_logger = loggerProvider.CreateLogger<DependencyInjectionContext>();
_logger.LogInformation("Building dalamud service container for {Assembly}",
typeof(DependencyInjectionContext).Assembly.FullName);
@ -73,7 +74,7 @@ namespace Pal.Client
.AddFilter("Microsoft.EntityFrameworkCore.Database", LogLevel.Warning)
.AddFilter("Grpc", LogLevel.Debug)
.ClearProviders()
.AddProvider(LoggerProvider));
.AddDalamudLogger(pluginLog));
// dalamud
_serviceCollection.AddSingleton<IDalamudPlugin>(plugin);
@ -87,6 +88,7 @@ namespace Pal.Client
_serviceCollection.AddSingleton(condition);
_serviceCollection.AddSingleton(commandManager);
_serviceCollection.AddSingleton(dataManager);
_serviceCollection.AddSingleton(gameInteropProvider);
_serviceCollection.AddSingleton(new WindowSystem(typeof(DependencyInjectionContext).AssemblyQualifiedName));
_sqliteConnectionString =
@ -117,9 +119,10 @@ namespace Pal.Client
// commands
_serviceCollection.AddScoped<PalConfigCommand>();
_serviceCollection.AddScoped<PalNearCommand>();
_serviceCollection.AddScoped<PalStatsCommand>();
_serviceCollection.AddScoped<PalTestConnectionCommand>();
_serviceCollection.AddScoped<ISubCommand, PalConfigCommand>();
_serviceCollection.AddScoped<ISubCommand, PalNearCommand>();
_serviceCollection.AddScoped<ISubCommand, PalStatsCommand>();
_serviceCollection.AddScoped<ISubCommand, PalTestConnectionCommand>();
// territory & marker related services
_serviceCollection.AddScoped<TerritoryState>();
@ -127,6 +130,7 @@ namespace Pal.Client
_serviceCollection.AddScoped<ChatService>();
_serviceCollection.AddScoped<FloorService>();
_serviceCollection.AddScoped<ImportService>();
_serviceCollection.AddScoped<ObjectTableDebug>();
// windows & related services
_serviceCollection.AddScoped<AgreementWindow>();
@ -188,4 +192,3 @@ namespace Pal.Client
SqliteConnection.ClearPool(sqliteConnection);
}
}
}

View File

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

View File

@ -3,8 +3,8 @@ using System.Runtime.InteropServices;
using System.Text;
using ImGuiNET;
namespace Pal.Client.Extensions
{
namespace Pal.Client.Extensions;
internal static class PalImGui
{
/// <summary>
@ -33,4 +33,3 @@ namespace Pal.Client.Extensions
choice = value;
}
}
}

View File

@ -1,7 +1,7 @@
using System;
namespace Pal.Client.Floors
{
namespace Pal.Client.Floors;
/// <summary>
/// This is a currently-visible marker.
/// </summary>
@ -26,4 +26,3 @@ namespace Pal.Client.Floors
return $"EphemeralLocation(Position={Position}, Type={Type})";
}
}
}

View File

@ -10,8 +10,8 @@ using Pal.Client.Floors.Tasks;
using Pal.Client.Net;
using Pal.Common;
namespace Pal.Client.Floors
{
namespace Pal.Client.Floors;
internal sealed class FloorService
{
private readonly IPalacePalConfiguration _configuration;
@ -160,4 +160,3 @@ namespace Pal.Client.Floors
}
}
}
}

View File

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

View File

@ -5,8 +5,8 @@ using Pal.Client.Rendering;
using Pal.Common;
using Palace;
namespace Pal.Client.Floors
{
namespace Pal.Client.Floors;
/// <summary>
/// Base class for <see cref="MemoryLocation"/> and <see cref="EphemeralLocation"/>.
/// </summary>
@ -26,6 +26,7 @@ namespace Pal.Client.Floors
Hoard,
SilverCoffer,
GoldCoffer,
}
public override bool Equals(object? obj)
@ -63,4 +64,3 @@ namespace Pal.Client.Floors
};
}
}
}

View File

@ -5,8 +5,8 @@ using Pal.Client.Configuration;
using Pal.Client.Scheduled;
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>
@ -60,4 +60,3 @@ namespace Pal.Client.Floors
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,8 +2,8 @@
using System.Collections.Generic;
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>
@ -52,4 +52,3 @@ namespace Pal.Client.Floors
return $"PersistentLocation(Position={Position}, Type={Type})";
}
}
}

View File

@ -4,8 +4,8 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Pal.Client.Database;
namespace Pal.Client.Floors.Tasks
{
namespace Pal.Client.Floors.Tasks;
internal abstract class DbTask<T>
where T : DbTask<T>
{
@ -24,18 +24,23 @@ namespace Pal.Client.Floors.Tasks
{
using var scope = _serviceScopeFactory.CreateScope();
ILogger<T> logger = scope.ServiceProvider.GetRequiredService<ILogger<T>>();
try
{
using var dbContext = scope.ServiceProvider.GetRequiredService<PalClientContext>();
Run(dbContext, logger);
}
catch (Exception e)
{
DependencyInjectionContext.LoggerProvider.CreateLogger<DbTask<T>>()
.LogError(e, "Failed to run DbTask");
logger.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);
}
}

View File

@ -7,8 +7,8 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Pal.Client.Database;
namespace Pal.Client.Floors.Tasks
{
namespace Pal.Client.Floors.Tasks;
internal sealed class LoadTerritory : DbTask<LoadTerritory>
{
private readonly Cleanup _cleanup;
@ -76,4 +76,3 @@ namespace Pal.Client.Floors.Tasks
};
}
}
}

View File

@ -5,8 +5,8 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Pal.Client.Database;
namespace Pal.Client.Floors.Tasks
{
namespace Pal.Client.Floors.Tasks;
internal sealed class MarkLocalSeen : DbTask<MarkLocalSeen>
{
private readonly MemoryTerritory _territory;
@ -24,13 +24,14 @@ namespace Pal.Client.Floors.Tasks
{
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);
List<int> localIds = _locations.Select(l => l.LocalId).Where(x => x != null).Cast<int>().ToList();
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));
dbContext.SaveChanges();
}
}
}
}

View File

@ -5,8 +5,8 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Pal.Client.Database;
namespace Pal.Client.Floors.Tasks
{
namespace Pal.Client.Floors.Tasks;
internal sealed class MarkRemoteSeen : DbTask<MarkRemoteSeen>
{
private readonly MemoryTerritory _territory;
@ -48,4 +48,3 @@ namespace Pal.Client.Floors.Tasks
}
}
}
}

View File

@ -6,8 +6,8 @@ using Microsoft.Extensions.Logging;
using Pal.Client.Database;
using Pal.Common;
namespace Pal.Client.Floors.Tasks
{
namespace Pal.Client.Floors.Tasks;
internal sealed class SaveNewLocations : DbTask<SaveNewLocations>
{
private readonly MemoryTerritory _territory;
@ -74,4 +74,3 @@ namespace Pal.Client.Floors.Tasks
};
}
}
}

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;
namespace Pal.Client.Floors
{
namespace Pal.Client.Floors;
public sealed class TerritoryState
{
private readonly ClientState _clientState;
private readonly Condition _condition;
private readonly IClientState _clientState;
private readonly ICondition _condition;
public TerritoryState(ClientState clientState, Condition condition)
public TerritoryState(IClientState clientState, ICondition condition)
{
_clientState = clientState;
_condition = condition;
@ -23,7 +23,6 @@ namespace Pal.Client.Floors
_clientState.IsLoggedIn
&& _condition[ConditionFlag.InDeepDungeon]
&& typeof(ETerritoryType).IsEnumDefined(_clientState.TerritoryType);
}
public enum PomanderState
@ -33,4 +32,3 @@ namespace Pal.Client.Floors
FoundOnCurrentFloor,
PomanderOfSafetyUsed,
}
}

View File

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

View File

@ -1,13 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
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
{
[JsonPropertyName("nameid")]
@ -42,7 +40,8 @@ namespace Pal.Client.Net
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");
}
}
@ -75,21 +74,6 @@ namespace Pal.Client.Net
throw new JsonException("bad token type");
}
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();
}
public override void Write(Utf8JsonWriter writer, List<string> 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 Grpc.Core;
using Grpc.Net.Client;
using Microsoft.Extensions.Logging;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
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.Properties;
using Pal.Client.Configuration;
using Version = System.Version;
namespace Pal.Client.Net;
namespace Pal.Client.Net
{
internal partial class RemoteApi
{
private static readonly Version PluginVersion = typeof(Plugin).Assembly.GetName().Version!;
private readonly SemaphoreSlim _connectLock = new(1, 1);
private async Task<(bool Success, string Error)> TryConnect(CancellationToken cancellationToken,
@ -73,7 +75,14 @@ namespace Pal.Client.Net
if (configuredAccount == null)
{
_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),
cancellationToken: cancellationToken);
if (createAccountReply.Success)
@ -115,7 +124,15 @@ namespace Pal.Client.Net
_logger.LogInformation("Logging in with account id {AccountId}",
configuredAccount.AccountId.ToPartialId());
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),
cancellationToken: cancellationToken);
@ -229,4 +246,3 @@ namespace Pal.Client.Net
public bool IsValid => IsLoggedIn && !IsExpired;
}
}
}

View File

@ -1,10 +1,10 @@
using Account;
using System;
using System;
using System.Threading;
using System.Threading.Tasks;
using Export;
namespace Pal.Client.Net;
namespace Pal.Client.Net
{
internal partial class RemoteApi
{
public async Task<(bool, ExportRoot)> DoExport(CancellationToken cancellationToken = default)
@ -16,8 +16,8 @@ namespace Pal.Client.Net
var exportReply = await exportClient.ExportAsync(new ExportRequest
{
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);
}
}
}

View File

@ -1,5 +1,4 @@
using Palace;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
@ -7,22 +6,27 @@ using System.Threading;
using System.Threading.Tasks;
using Pal.Client.Database;
using Pal.Client.Floors;
using Palace;
namespace Pal.Client.Net;
namespace Pal.Client.Net
{
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))
return (false, new());
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());
}
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)
return (true, new());
@ -42,11 +46,13 @@ namespace Pal.Client.Net
Y = m.Position.Y,
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());
}
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)
return true;
@ -59,7 +65,8 @@ namespace Pal.Client.Net
foreach (var marker in locations)
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;
}
@ -80,8 +87,9 @@ namespace Pal.Client.Net
return new(false, new List<FloorStatistics>());
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());
}
}
}

View File

@ -1,12 +1,12 @@
using System;
using Dalamud.Logging;
using Grpc.Core;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using Dalamud.Logging;
using Grpc.Core;
using Microsoft.Extensions.Logging;
namespace Pal.Client.Net
{
namespace Pal.Client.Net;
internal partial class RemoteApi
{
private Metadata UnauthorizedHeaders() => new()
@ -55,4 +55,3 @@ namespace Pal.Client.Net
#endif
}
}
}

View File

@ -1,19 +1,19 @@
using Dalamud.Logging;
using System;
using Dalamud.Game.Gui;
using Dalamud.Logging;
using Grpc.Net.Client;
using Microsoft.Extensions.Logging;
using System;
using Dalamud.Game.Gui;
using Pal.Client.Configuration;
using Pal.Client.DependencyInjection;
namespace Pal.Client.Net
{
namespace Pal.Client.Net;
internal sealed partial class RemoteApi : IDisposable
{
#if DEBUG
public const string RemoteUrl = "http://localhost:5415";
#else
public const string RemoteUrl = "https://pal.liza.sh";
public const string RemoteUrl = "https://connect.palacepal.com";
#endif
private readonly string _userAgent =
$"{typeof(RemoteApi).Assembly.GetName().Name?.Replace(" ", "")}/{typeof(RemoteApi).Assembly.GetName().Version?.ToString(2)}";
@ -49,4 +49,3 @@ namespace Pal.Client.Net
_channel = null;
}
}
}

View File

@ -1,28 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Dalamud.NET.Sdk/9.0.2">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<LangVersion>11.0</LangVersion>
<Nullable>enable</Nullable>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<PlatformTarget>x64</PlatformTarget>
<Version>6.0</Version>
<AssemblyName>Palace Pal</AssemblyName>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<PlatformTarget>x64</PlatformTarget>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<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>
<Import Project="..\vendor\LLib\LLib.targets"/>
<Import Project="..\vendor\LLib\RenameZip.targets"/>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<OutputPath>dist</OutputPath>
</PropertyGroup>
@ -36,28 +23,26 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="DalamudPackager" Version="2.1.10" />
<PackageReference Include="Google.Protobuf" Version="3.21.12" />
<PackageReference Include="Grpc.Net.Client" Version="2.51.0" />
<PackageReference Include="GitInfo" Version="2.3.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Grpc.Tools" Version="2.51.0">
<PackageReference Include="Dalamud.Extensions.MicrosoftLogging" Version="4.0.1"/>
<PackageReference Include="Google.Protobuf" Version="3.27.2" />
<PackageReference Include="Grpc.Net.Client" Version="2.63.0"/>
<PackageReference Include="Grpc.Tools" Version="2.64.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.3">
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.6" Condition="'$(Configuration)' == 'EF'">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0"/>
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Pal.Common\Pal.Common.csproj"/>
<ProjectReference Include="..\vendor\ECommons\ECommons\ECommons.csproj"/>
<ProjectReference Include="..\vendor\LLib\LLib.csproj" />
</ItemGroup>
<ItemGroup>
@ -66,42 +51,6 @@
<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>
<EmbeddedResource Update="Properties\Localization.resx">
<Generator>ResXFileCodeGenerator</Generator>
@ -114,17 +63,6 @@
</Compile>
</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">
<RemoveDir Directories="dist"/>
</Target>

View File

@ -3,7 +3,12 @@
"Author": "Liza Carvelli",
"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.",
"RepoUrl": "https://github.com/carvelli/PalacePal",
"IconUrl": "https://raw.githubusercontent.com/carvelli/Dalamud-Plugins/master/dist/Palace Pal.png",
"Tags": [ "potd", "palace", "hoh", "splatoon" ]
"RepoUrl": "https://git.carvel.li/liza/PalacePal",
"IconUrl": "https://plugins.carvel.li/icons/PalacePal.png",
"Tags": [
"potd",
"palace",
"hoh",
"splatoon"
]
}

View File

@ -1,25 +1,27 @@
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using Pal.Client.Rendering;
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Extensions.MicrosoftLogging;
using Dalamud.Game.Command;
using Dalamud.Game.Gui;
using Pal.Client.Properties;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using ECommons;
using ECommons.DalamudServices;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Pal.Client.Commands;
using Pal.Client.Configuration;
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 .
@ -29,33 +31,35 @@ namespace Pal.Client
{
private readonly CancellationTokenSource _initCts = new();
private readonly DalamudPluginInterface _pluginInterface;
private readonly CommandManager _commandManager;
private readonly ClientState _clientState;
private readonly ChatGui _chatGui;
private readonly Framework _framework;
private readonly IDalamudPluginInterface _pluginInterface;
private readonly ICommandManager _commandManager;
private readonly IClientState _clientState;
private readonly IChatGui _chatGui;
private readonly IFramework _framework;
private readonly TaskCompletionSource<IServiceScope> _rootScopeCompletionSource = new();
private ELoadState _loadState = ELoadState.Initializing;
private DependencyInjectionContext? _dependencyInjectionContext;
private ILogger _logger = DependencyInjectionContext.LoggerProvider.CreateLogger<Plugin>();
private ILogger _logger;
private WindowSystem? _windowSystem;
private IServiceScope? _rootScope;
private Action? _loginAction;
public Plugin(
DalamudPluginInterface pluginInterface,
CommandManager commandManager,
ClientState clientState,
ChatGui chatGui,
Framework framework)
IDalamudPluginInterface pluginInterface,
ICommandManager commandManager,
IClientState clientState,
IChatGui chatGui,
IFramework framework,
IPluginLog pluginLog)
{
_pluginInterface = pluginInterface;
_commandManager = commandManager;
_clientState = clientState;
_chatGui = chatGui;
_framework = framework;
_logger = new DalamudLoggerProvider(pluginLog).CreateLogger<Plugin>();
// set up the current UI language before creating anything
Localization.Culture = new CultureInfo(_pluginInterface.UiLanguage);
@ -65,11 +69,13 @@ namespace Pal.Client
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());
}
public string Name => Localization.Palace_Pal;
private async Task CreateDependencyContext()
{
try
@ -96,12 +102,10 @@ namespace Pal.Client
_rootScopeCompletionSource.SetResult(_rootScope);
_loadState = ELoadState.Loaded;
}
catch (ObjectDisposedException e)
{
_rootScopeCompletionSource.SetException(e);
_loadState = ELoadState.Error;
}
catch (OperationCanceledException e)
catch (Exception e) when (e is ObjectDisposedException
or OperationCanceledException
or RepoVerification.RepoVerificationFailedException
|| (e is FileLoadException && _pluginInterface.IsDev))
{
_rootScopeCompletionSource.SetException(e);
_loadState = ELoadState.Error;
@ -129,7 +133,7 @@ namespace Pal.Client
_loginAction = loginAction;
}
private void Login(object? sender, EventArgs eventArgs)
private void Login()
{
_loginAction?.Invoke();
_loginAction = null;
@ -165,39 +169,24 @@ namespace Pal.Client
return;
}
var sp = rootScope.ServiceProvider;
switch (arguments)
Action<string> commandHandler = rootScope.ServiceProvider
.GetRequiredService<IEnumerable<ISubCommand>>()
.SelectMany(cmd => cmd.GetHandlers())
.Where(cmd => cmd.Key == arguments.ToLowerInvariant())
.Select(cmd => cmd.Value)
.SingleOrDefault(missingCommand =>
{
case "":
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,
chat.Error(string.Format(Localization.Command_pal_UnknownSubcommand, missingCommand,
command));
break;
}
});
commandHandler.Invoke(arguments);
}
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}"));
}
});
}
@ -244,4 +233,3 @@ namespace Pal.Client
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>
/// Looks up a localized string similar to Hoard Coffer color.
/// </summary>
@ -357,7 +394,7 @@ namespace Pal.Client.Properties {
}
/// <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..
/// </summary>
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>
/// Looks up a localized string similar to Please finish the initial setup first..
/// </summary>

View File

@ -18,10 +18,39 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<!-- 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 -->
<!-- /pal commands -->
<!-- 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 -->
<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: Import -->
<!-- Config Window: Export -->

View File

@ -142,6 +142,20 @@
<value>塗りつぶす</value>
<comment>Whether silver coffers should only be drawn with the circle outline or as filled circle.</comment>
</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 -->
<data name="ConfigTab_Community" xml:space="preserve">
<value>コミュニティ</value>

View File

@ -48,6 +48,7 @@
</data>
<data name="Error_LoadFailed" xml:space="preserve">
<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 name="Error_WrongRepository" xml:space="preserve">
<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>
<comment>Shown when /pal stats produces a server-side error, and the statistics window can't be loaded.</comment>
</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'. -->
<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>
</data>
<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>
</data>
<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>
<comment>Whether silver coffers should only be drawn with the circle outline or as filled circle.</comment>
</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 -->
<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:
```shell
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
{
TrapHoard,
RegularCoffers,
Test,
}
}

View File

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

View File

@ -3,8 +3,8 @@ using System.Numerics;
using Pal.Client.Configuration;
using Pal.Client.Floors;
namespace Pal.Client.Rendering
{
namespace Pal.Client.Rendering;
internal interface IRenderer
{
ERenderer GetConfigValue();
@ -13,8 +13,7 @@ namespace Pal.Client.Rendering
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);
}
}

View File

@ -1,8 +1,8 @@
using System.Collections.Generic;
using Pal.Client.Floors;
namespace Pal.Client.Rendering
{
namespace Pal.Client.Rendering;
internal sealed class MarkerConfig
{
private static readonly MarkerConfig EmptyConfig = new();
@ -12,6 +12,7 @@ namespace Pal.Client.Rendering
{ MemoryLocation.EType.Trap, new MarkerConfig { Radius = 1.7f } },
{ MemoryLocation.EType.Hoard, new MarkerConfig { Radius = 1.7f, OffsetY = -0.03f } },
{ MemoryLocation.EType.SilverCoffer, new MarkerConfig { Radius = 1f } },
{ MemoryLocation.EType.GoldCoffer, new MarkerConfig { Radius = 1f } },
};
public float OffsetY { get; private init; }
@ -20,4 +21,3 @@ namespace Pal.Client.Rendering
public static MarkerConfig ForType(MemoryLocation.EType type) =>
MarkerConfigs.GetValueOrDefault(type, EmptyConfig);
}
}

View File

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

View File

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

View File

@ -1,18 +1,16 @@
using Dalamud.Interface;
using ImGuiNET;
using System;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Game.ClientState;
using Dalamud.Game.Gui;
using Dalamud.Interface.Utility;
using Dalamud.Plugin.Services;
using ImGuiNET;
using Pal.Client.Configuration;
using Pal.Client.DependencyInjection;
using Pal.Client.Floors;
namespace Pal.Client.Rendering
{
namespace Pal.Client.Rendering;
/// <summary>
/// Simple renderer that only draws basic stuff.
///
@ -24,13 +22,13 @@ namespace Pal.Client.Rendering
{
private const int SegmentCount = 20;
private readonly ClientState _clientState;
private readonly GameGui _gameGui;
private readonly IClientState _clientState;
private readonly IGameGui _gameGui;
private readonly IPalacePalConfiguration _configuration;
private readonly TerritoryState _territoryState;
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)
{
_clientState = clientState;
@ -54,13 +52,15 @@ namespace Pal.Client.Rendering
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);
return new SimpleElement
{
Type = type,
Position = pos + new Vector3(0, config.OffsetY, 0),
Enabled = enabled,
Color = color,
Radius = config.Radius,
Fill = fill,
@ -77,10 +77,12 @@ namespace Pal.Client.Rendering
(SimpleElement)CreateElement(
MemoryLocation.EType.Trap,
_clientState.LocalPlayer?.Position ?? default,
true,
trapColor),
(SimpleElement)CreateElement(
MemoryLocation.EType.Hoard,
_clientState.LocalPlayer?.Position ?? default,
true,
hoardColor)
},
ExpiresAt = Environment.TickCount64 + RenderData.TestLayerTimeout
@ -120,7 +122,7 @@ namespace Pal.Client.Rendering
private void Draw(SimpleElement e)
{
if (e.Color == RenderData.ColorInvisible)
if (!e.Enabled)
return;
switch (e.Type)
@ -181,7 +183,7 @@ namespace Pal.Client.Rendering
public required IReadOnlyList<SimpleElement> Elements { get; init; }
public long ExpiresAt { get; init; } = long.MaxValue;
public bool IsValid(ClientState clientState) =>
public bool IsValid(IClientState clientState) =>
TerritoryType == clientState.TerritoryType && ExpiresAt >= Environment.TickCount64;
public void Dispose()
@ -196,9 +198,9 @@ namespace Pal.Client.Rendering
public bool IsValid { get; set; } = true;
public required MemoryLocation.EType Type { get; init; }
public required Vector3 Position { get; init; }
public required bool Enabled { get; set; }
public required uint Color { get; set; }
public required float Radius { get; init; }
public required bool Fill { get; init; }
}
}
}

View File

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

View File

@ -2,8 +2,8 @@
using Dalamud.Logging;
using Microsoft.Extensions.Logging;
namespace Pal.Client.Scheduled
{
namespace Pal.Client.Scheduled;
internal interface IQueueOnFrameworkThread
{
internal interface IHandler
@ -37,4 +37,3 @@ namespace Pal.Client.Scheduled
}
}
}
}

View File

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

View File

@ -1,17 +1,17 @@
using Account;
using Pal.Common;
using System;
using System;
using System.IO;
using System.Threading.Tasks;
using Export;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Pal.Client.Database;
using Pal.Client.DependencyInjection;
using Pal.Client.Properties;
using Pal.Client.Windows;
using Pal.Common;
namespace Pal.Client.Scheduled;
namespace Pal.Client.Scheduled
{
internal sealed class QueuedImport : IQueueOnFrameworkThread
{
private ExportRoot Export { get; }
@ -120,4 +120,3 @@ namespace Pal.Client.Scheduled
}
}
}
}

View File

@ -11,8 +11,8 @@ using Pal.Client.Floors.Tasks;
using Pal.Client.Net;
using Pal.Common;
namespace Pal.Client.Scheduled
{
namespace Pal.Client.Scheduled;
internal sealed class QueuedSyncResponse : IQueueOnFrameworkThread
{
public required SyncType Type { get; init; }
@ -158,4 +158,3 @@ namespace Pal.Client.Scheduled
Download,
MarkSeen,
}
}

View File

@ -7,8 +7,8 @@ using Pal.Client.Floors;
using Pal.Client.Windows;
using Pal.Common;
namespace Pal.Client.Scheduled
{
namespace Pal.Client.Scheduled;
internal sealed class QueuedUndoImport : IQueueOnFrameworkThread
{
public QueuedUndoImport(Guid exportId)
@ -39,4 +39,3 @@ namespace Pal.Client.Scheduled
}
}
}
}

View File

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

View File

@ -1,31 +1,33 @@
using Account;
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;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Threading;
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 Pal.Client.Extensions;
using Pal.Client.Properties;
using Pal.Client.Configuration;
using Pal.Client.Database;
using Pal.Client.DependencyInjection;
using Pal.Client.Extensions;
using Pal.Client.Floors;
using Pal.Client.Net;
using Pal.Client.Properties;
using Pal.Client.Rendering;
using Pal.Client.Scheduled;
namespace Pal.Client.Windows
{
internal sealed class ConfigWindow : Window, ILanguageChanged, IDisposable
namespace Pal.Client.Windows;
internal sealed class ConfigWindow : LWindow, ILanguageChanged, IDisposable
{
private const string WindowId = "###PalPalaceConfig";
@ -47,6 +49,7 @@ namespace Pal.Client.Windows
private ConfigurableMarker _trapConfig = new();
private ConfigurableMarker _hoardConfig = new();
private ConfigurableMarker _silverConfig = new();
private ConfigurableMarker _goldConfig = new();
private string? _connectionText;
private bool _switchToCommunityTab;
@ -96,6 +99,12 @@ namespace Pal.Client.Windows
Position = new Vector2(300, 300);
PositionCondition = ImGuiCond.FirstUseEver;
SizeConstraints = new WindowSizeConstraints
{
MinimumSize = new Vector2(300, 300),
MaximumSize = new Vector2(9999, 9999),
};
_importDialog = new FileDialogManager
{ AddedWindowFlags = ImGuiWindowFlags.NoCollapse | ImGuiWindowFlags.NoDocking };
_exportDialog = new FileDialogManager
@ -124,6 +133,7 @@ namespace Pal.Client.Windows
_trapConfig = new ConfigurableMarker(_configuration.DeepDungeons.Traps);
_hoardConfig = new ConfigurableMarker(_configuration.DeepDungeons.HoardCoffers);
_silverConfig = new ConfigurableMarker(_configuration.DeepDungeons.SilverCoffers);
_goldConfig = new ConfigurableMarker(_configuration.DeepDungeons.GoldCoffers);
_connectionText = null;
UpdateLastImport();
@ -162,6 +172,7 @@ namespace Pal.Client.Windows
_configuration.DeepDungeons.Traps = _trapConfig.Build();
_configuration.DeepDungeons.HoardCoffers = _hoardConfig.Build();
_configuration.DeepDungeons.SilverCoffers = _silverConfig.Build();
_configuration.DeepDungeons.GoldCoffers = _goldConfig.Build();
_configurationManager.Save(_configuration);
@ -174,6 +185,7 @@ namespace Pal.Client.Windows
{
if (ImGui.BeginTabItem($"{Localization.ConfigTab_DeepDungeons}###TabDeepDungeons"))
{
ImGui.PushID("trap");
ImGui.Checkbox(Localization.Config_Traps_Show, ref _trapConfig.Show);
ImGui.Indent();
ImGui.BeginDisabled(!_trapConfig.Show);
@ -184,9 +196,11 @@ namespace Pal.Client.Windows
ImGuiComponents.HelpMarker(Localization.Config_Traps_HideImpossible_ToolTip);
ImGui.EndDisabled();
ImGui.Unindent();
ImGui.PopID();
ImGui.Separator();
ImGui.PushID("hoard");
ImGui.Checkbox(Localization.Config_HoardCoffers_Show, ref _hoardConfig.Show);
ImGui.Indent();
ImGui.BeginDisabled(!_hoardConfig.Show);
@ -199,9 +213,11 @@ namespace Pal.Client.Windows
ImGuiComponents.HelpMarker(Localization.Config_HoardCoffers_HideImpossible_ToolTip);
ImGui.EndDisabled();
ImGui.Unindent();
ImGui.PopID();
ImGui.Separator();
ImGui.PushID("silver");
ImGui.Checkbox(Localization.Config_SilverCoffer_Show, ref _silverConfig.Show);
ImGuiComponents.HelpMarker(Localization.Config_SilverCoffers_ToolTip);
ImGui.Indent();
@ -212,6 +228,22 @@ namespace Pal.Client.Windows
ImGui.Checkbox(Localization.Config_SilverCoffer_Filled, ref _silverConfig.Fill);
ImGui.EndDisabled();
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();
@ -260,11 +292,13 @@ namespace Pal.Client.Windows
ImGui.TextWrapped(Localization.Config_ImportExplanation1);
ImGui.TextWrapped(Localization.Config_ImportExplanation2);
ImGui.TextWrapped(Localization.Config_ImportExplanation3);
/* FIXME
ImGui.Separator();
ImGui.TextWrapped(string.Format(Localization.Config_ImportDownloadLocation,
"https://github.com/carvelli/PalacePal/releases/"));
if (ImGui.Button(Localization.Config_Import_VisitGitHub))
GenericHelpers.ShellStart("https://github.com/carvelli/PalacePal/releases/latest");
*/
ImGui.Separator();
ImGui.Text(Localization.Config_SelectImportFile);
ImGui.SameLine();
@ -408,6 +442,15 @@ namespace Pal.Client.Windows
$"{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 Intuition: {_territoryState.PomanderOfIntuition}");
}
@ -543,4 +586,3 @@ namespace Pal.Client.Windows
}
}
}
}

View File

@ -1,15 +1,15 @@
using Dalamud.Interface.Windowing;
using ImGuiNET;
using Pal.Common;
using Palace;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Interface.Windowing;
using ImGuiNET;
using Pal.Client.Properties;
using Pal.Common;
using Palace;
namespace Pal.Client.Windows;
namespace Pal.Client.Windows
{
internal sealed class StatisticsWindow : Window, IDisposable, ILanguageChanged
{
private const string WindowId = "###PalacePalStats";
@ -49,6 +49,8 @@ namespace Pal.Client.Windows
ETerritoryType.Palace_191_200);
DrawDungeonStats("Heaven on High", Localization.HeavenOnHigh, ETerritoryType.HeavenOnHigh_1_10,
ETerritoryType.HeavenOnHigh_91_100);
DrawDungeonStats("Eureka Orthos", Localization.EurekaOrthos, ETerritoryType.EurekaOrthos_1_10,
ETerritoryType.EurekaOrthos_91_100);
}
}
@ -121,4 +123,3 @@ namespace Pal.Client.Windows
}
}
}
}

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,8 +1,8 @@
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
namespace Pal.Common
{
namespace Pal.Common;
[SuppressMessage("ReSharper", "UnusedMember.Global")]
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum ETerritoryType : ushort
@ -47,6 +47,16 @@ namespace Pal.Common
[Display(Order = 9)]
HeavenOnHigh_81_90 = 775,
[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,8 +1,10 @@
using System.ComponentModel.DataAnnotations;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
namespace Pal.Common
{
namespace Pal.Common;
public static class EnumExtensions
{
public static int? GetOrder(this Enum e)
@ -13,4 +15,3 @@ namespace Pal.Common
return attribute?.Order;
}
}
}

View File

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

View File

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

View File

@ -1,7 +1,8 @@
using System.Numerics;
using System;
using System.Numerics;
namespace Pal.Common;
namespace Pal.Common
{
public class PalaceMath
{
private static readonly Vector3 ScaleFactor = new(5);
@ -19,4 +20,3 @@ namespace Pal.Common
return HashCode.Combine((int)v.X, (int)v.Y, (int)v.Z);
}
}
}

View File

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

View File

@ -1,6 +1,6 @@
syntax = "proto3";
package account;
package export;
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
VisualStudioVersion = 17.3.32929.385
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
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Pal.Client", "Pal.Client\Pal.Client.csproj", "{7F1985B3-D376-4A91-BC9B-46C2A860F9EF}"
EndProject
@ -13,6 +13,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject
Dockerfile = Dockerfile
README.md = README.md
.editorconfig = .editorconfig
EndProjectSection
EndProject
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
EndProjectSection
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
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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|x64.ActiveCfg = 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
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -5,15 +5,14 @@ Shows possible trap & hoard coffer locations in Palace of the Dead & Heaven on H
## Installation
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.
Please check [Splatoon's Installation Instructions](https://github.com/NightmareXIV/MyDalamudPlugins#installation).
## Translation
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,
[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