Compare commits
32 Commits
d5f22da734
...
1.7.1.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 60c1668492 | |||
| 4f07dba409 | |||
| 003fc77628 | |||
| 41b5122e12 | |||
| b9524dbe73 | |||
| 166fc0be91 | |||
| 6519ba6b6c | |||
| c1dbe3663e | |||
| e52fb6a452 | |||
| 14ee5fe6ce | |||
| 6b7f9a6ea0 | |||
| 74c83abe91 | |||
| 00cc07efa4 | |||
| 4853afd1eb | |||
| 5cac38f446 | |||
| 30aab1739d | |||
| a7e8ff9a19 | |||
| 545b06aad6 | |||
| 062d8bf6b2 | |||
| a8a2d8a48f | |||
| 9b0134aa7d | |||
| 243b0b8300 | |||
| b41532d5af | |||
|
|
bef5b1be34 | ||
|
|
80c186b6a1 | ||
|
|
7a2d4a5978 | ||
|
|
1768d68df2 | ||
|
|
2168fa91ce | ||
|
|
ae1837b8f5 | ||
|
|
d97c380e67 | ||
|
|
997977a978 | ||
|
|
e39694f215 |
97
.gitea/workflows/release-clubpenguin-on-tag.yml
Normal file
97
.gitea/workflows/release-clubpenguin-on-tag.yml
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
name: Build and Update Repo
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
PLUGIN_NAME: ClubPenguinSync
|
||||||
|
DOTNET_VERSION: 9.x
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-update-repo:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Club Penguin
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Setup .NET 9 SDK
|
||||||
|
uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: 9.x
|
||||||
|
|
||||||
|
- name: Download Dalamud
|
||||||
|
run: |
|
||||||
|
cd /
|
||||||
|
mkdir -p ~/.xlcore/dalamud/Hooks/dev
|
||||||
|
curl -O https://goatcorp.github.io/dalamud-distrib/stg/latest.zip
|
||||||
|
unzip latest.zip -d ~/.xlcore/dalamud/Hooks/dev
|
||||||
|
|
||||||
|
- name: Build Club Penguin
|
||||||
|
run: |
|
||||||
|
dotnet restore
|
||||||
|
dotnet build --configuration Release --no-restore
|
||||||
|
|
||||||
|
- name: Prepare Club Penguin Repo
|
||||||
|
run: |
|
||||||
|
mkdir -p output
|
||||||
|
mv MareSynchronos/bin/x64/Release/ClubPenguinSync/latest.zip /repo/download/ClubPenguinSync-${{ gitea.ref_name }}.zip
|
||||||
|
|
||||||
|
- name: Update repo.json with version
|
||||||
|
env:
|
||||||
|
VERSION: ${{ gitea.ref_name }}
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
|
||||||
|
pluginJsonPath="MareSynchronos/bin/x64/Release/${PLUGIN_NAME}.json"
|
||||||
|
repoJsonPath="/repo/repo.json"
|
||||||
|
version="${VERSION}"
|
||||||
|
downloadUrl="https://clubpenguin.drgn.rocks/download/ClubPenguinSync-${{ gitea.ref_name }}.zip"
|
||||||
|
|
||||||
|
# Read plugin JSON
|
||||||
|
pluginJson=$(cat "$pluginJsonPath")
|
||||||
|
internalName=$(jq -r '.InternalName' <<< "$pluginJson")
|
||||||
|
dalamudApiLevel=$(jq -r '.DalamudApiLevel' <<< "$pluginJson")
|
||||||
|
|
||||||
|
# Read repo JSON (force array if not already)
|
||||||
|
repoJsonRaw=$(cat "$repoJsonPath")
|
||||||
|
if echo "$repoJsonRaw" | jq 'type' | grep -q '"array"'; then
|
||||||
|
repoJson="$repoJsonRaw"
|
||||||
|
else
|
||||||
|
repoJson="[$repoJsonRaw]"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update matching plugin entry
|
||||||
|
updatedRepoJson=$(jq \
|
||||||
|
--arg internalName "$internalName" \
|
||||||
|
--arg dalamudApiLevel "$dalamudApiLevel" \
|
||||||
|
--arg version "$version" \
|
||||||
|
--arg downloadUrl "$downloadUrl" \
|
||||||
|
'
|
||||||
|
map(
|
||||||
|
if .InternalName == $internalName
|
||||||
|
then
|
||||||
|
.DalamudApiLevel = $dalamudApiLevel
|
||||||
|
| .TestingDalamudApiLevel = $dalamudApiLevel
|
||||||
|
| .AssemblyVersion = $version
|
||||||
|
| .TestingAssemblyVersion = $version
|
||||||
|
| .DownloadLinkInstall = $downloadUrl
|
||||||
|
| .DownloadLinkTesting = $downloadUrl
|
||||||
|
| .DownloadLinkUpdate = $downloadUrl
|
||||||
|
else
|
||||||
|
.
|
||||||
|
end
|
||||||
|
)
|
||||||
|
' <<< "$repoJson")
|
||||||
|
|
||||||
|
# Write back to file
|
||||||
|
echo "$updatedRepoJson" > "$repoJsonPath"
|
||||||
|
# Output the content of the file
|
||||||
|
cat "$repoJsonPath"
|
||||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -1,12 +1,12 @@
|
|||||||
[submodule "MareAPI"]
|
[submodule "MareAPI"]
|
||||||
path = MareAPI
|
path = MareAPI
|
||||||
url = https://github.com/loporrit/MareAPI.git
|
url = https://git.drgn.rocks/t0w0bi/ClubPenguinApi.git
|
||||||
[submodule "Penumbra.Api"]
|
[submodule "Penumbra.Api"]
|
||||||
path = Penumbra.Api
|
path = Penumbra.Api
|
||||||
url = https://github.com/loporrit/Penumbra.Api.git
|
url = https://github.com/Ottermandias/Penumbra.Api.git
|
||||||
[submodule "Glamourer.Api"]
|
[submodule "Glamourer.Api"]
|
||||||
path = Glamourer.Api
|
path = Glamourer.Api
|
||||||
url = https://github.com/loporrit/Glamourer.Api.git
|
url = https://github.com/Ottermandias/Glamourer.Api.git
|
||||||
[submodule "BunnyWhispers"]
|
[submodule "BunnyWhispers"]
|
||||||
path = BunnyWhispers
|
path = BunnyWhispers
|
||||||
url = https://github.com/loporrit/BunnyWhispers.git
|
url = https://github.com/loporrit/BunnyWhispers.git
|
||||||
|
|||||||
Submodule Glamourer.Api updated: 2fb517e2bb...54c1944dc7
@@ -4,6 +4,10 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
|||||||
VisualStudioVersion = 17.1.32328.378
|
VisualStudioVersion = 17.1.32328.378
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MareSynchronos", "MareSynchronos\MareSynchronos.csproj", "{13C812E9-0D42-4B95-8646-40EEBF30636F}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MareSynchronos", "MareSynchronos\MareSynchronos.csproj", "{13C812E9-0D42-4B95-8646-40EEBF30636F}"
|
||||||
|
ProjectSection(ProjectDependencies) = postProject
|
||||||
|
{7A858094-ABEF-424B-9133-7CFC9B03B9E4} = {7A858094-ABEF-424B-9133-7CFC9B03B9E4}
|
||||||
|
{8A8069A2-31D7-4158-B29C-479F17FEFBB7} = {8A8069A2-31D7-4158-B29C-479F17FEFBB7}
|
||||||
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MareSynchronos.API", "MareAPI\MareSynchronosAPI\MareSynchronos.API.csproj", "{5A0B7434-8D89-4E90-B55C-B4A7AE1A6ADE}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MareSynchronos.API", "MareAPI\MareSynchronosAPI\MareSynchronos.API.csproj", "{5A0B7434-8D89-4E90-B55C-B4A7AE1A6ADE}"
|
||||||
EndProject
|
EndProject
|
||||||
@@ -12,6 +16,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
.editorconfig = .editorconfig
|
.editorconfig = .editorconfig
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Glamourer.Api", "Glamourer.Api\Glamourer.Api.csproj", "{8A8069A2-31D7-4158-B29C-479F17FEFBB7}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Penumbra.Api", "Penumbra.Api\Penumbra.Api.csproj", "{7A858094-ABEF-424B-9133-7CFC9B03B9E4}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chaos.NaCl", "BunnyWhispers\Chaos.NaCl\Chaos.NaCl.csproj", "{C4E4A934-A1CD-4CF7-B31E-4F0782BA8458}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -36,6 +46,30 @@ Global
|
|||||||
{5A0B7434-8D89-4E90-B55C-B4A7AE1A6ADE}.Release|Any CPU.Build.0 = Release|Any CPU
|
{5A0B7434-8D89-4E90-B55C-B4A7AE1A6ADE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{5A0B7434-8D89-4E90-B55C-B4A7AE1A6ADE}.Release|x64.ActiveCfg = Release|Any CPU
|
{5A0B7434-8D89-4E90-B55C-B4A7AE1A6ADE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
{5A0B7434-8D89-4E90-B55C-B4A7AE1A6ADE}.Release|x64.Build.0 = Release|Any CPU
|
{5A0B7434-8D89-4E90-B55C-B4A7AE1A6ADE}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{8A8069A2-31D7-4158-B29C-479F17FEFBB7}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
|
{8A8069A2-31D7-4158-B29C-479F17FEFBB7}.Debug|Any CPU.Build.0 = Debug|x64
|
||||||
|
{8A8069A2-31D7-4158-B29C-479F17FEFBB7}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{8A8069A2-31D7-4158-B29C-479F17FEFBB7}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{8A8069A2-31D7-4158-B29C-479F17FEFBB7}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
|
{8A8069A2-31D7-4158-B29C-479F17FEFBB7}.Release|Any CPU.Build.0 = Release|x64
|
||||||
|
{8A8069A2-31D7-4158-B29C-479F17FEFBB7}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{8A8069A2-31D7-4158-B29C-479F17FEFBB7}.Release|x64.Build.0 = Release|x64
|
||||||
|
{7A858094-ABEF-424B-9133-7CFC9B03B9E4}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||||
|
{7A858094-ABEF-424B-9133-7CFC9B03B9E4}.Debug|Any CPU.Build.0 = Debug|x64
|
||||||
|
{7A858094-ABEF-424B-9133-7CFC9B03B9E4}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{7A858094-ABEF-424B-9133-7CFC9B03B9E4}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{7A858094-ABEF-424B-9133-7CFC9B03B9E4}.Release|Any CPU.ActiveCfg = Release|x64
|
||||||
|
{7A858094-ABEF-424B-9133-7CFC9B03B9E4}.Release|Any CPU.Build.0 = Release|x64
|
||||||
|
{7A858094-ABEF-424B-9133-7CFC9B03B9E4}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{7A858094-ABEF-424B-9133-7CFC9B03B9E4}.Release|x64.Build.0 = Release|x64
|
||||||
|
{C4E4A934-A1CD-4CF7-B31E-4F0782BA8458}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C4E4A934-A1CD-4CF7-B31E-4F0782BA8458}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C4E4A934-A1CD-4CF7-B31E-4F0782BA8458}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{C4E4A934-A1CD-4CF7-B31E-4F0782BA8458}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{C4E4A934-A1CD-4CF7-B31E-4F0782BA8458}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C4E4A934-A1CD-4CF7-B31E-4F0782BA8458}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C4E4A934-A1CD-4CF7-B31E-4F0782BA8458}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{C4E4A934-A1CD-4CF7-B31E-4F0782BA8458}.Release|x64.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
14
MareSynchronos/ClubPenguinSync.json
Normal file
14
MareSynchronos/ClubPenguinSync.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"Author": "tb",
|
||||||
|
"Name": "Club Penguin Sync",
|
||||||
|
"Punchline": "Social modding for cool penguins!",
|
||||||
|
"Description": "This plugin will synchronize your Penumbra mods and current Glamourer state with other paired clients automatically.",
|
||||||
|
"InternalName": "ClubPenguinSync",
|
||||||
|
"ApplicableVersion": "any",
|
||||||
|
"Tags": [
|
||||||
|
"customization"
|
||||||
|
],
|
||||||
|
"IconUrl": "https://clubpenguin.drgn.rocks/icon.png",
|
||||||
|
"RepoUrl": "https://git.drgn.rocks/t0w0bi/ClubPenguinClient/",
|
||||||
|
"CanUnloadAsync": true
|
||||||
|
}
|
||||||
@@ -631,7 +631,7 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
|
|||||||
if (string.IsNullOrEmpty(_configService.Current.CacheFolder) || !Directory.Exists(_configService.Current.CacheFolder))
|
if (string.IsNullOrEmpty(_configService.Current.CacheFolder) || !Directory.Exists(_configService.Current.CacheFolder))
|
||||||
{
|
{
|
||||||
cacheDirExists = false;
|
cacheDirExists = false;
|
||||||
Logger.LogWarning("Loporrit Cache directory is not set or does not exist.");
|
Logger.LogWarning("Club Penguin Cache directory is not set or does not exist.");
|
||||||
}
|
}
|
||||||
if (!penDirExists || !cacheDirExists)
|
if (!penDirExists || !cacheDirExists)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -465,7 +465,7 @@ public sealed class FileCacheManager : IHostedService
|
|||||||
if (!_ipcManager.Penumbra.APIAvailable || string.IsNullOrEmpty(_ipcManager.Penumbra.ModDirectory))
|
if (!_ipcManager.Penumbra.APIAvailable || string.IsNullOrEmpty(_ipcManager.Penumbra.ModDirectory))
|
||||||
{
|
{
|
||||||
_mareMediator.Publish(new NotificationMessage("Penumbra not connected",
|
_mareMediator.Publish(new NotificationMessage("Penumbra not connected",
|
||||||
"Could not load local file cache data. Penumbra is not connected or not properly set up. Please enable and/or configure Penumbra properly to use Loporrit. After, reload Loporrit in the Plugin installer.",
|
"Could not load local file cache data. Penumbra is not connected or not properly set up. Please enable and/or configure Penumbra properly to use Club Penguin Sync. After, reload Club Penguin Sync in the Plugin installer.",
|
||||||
MareConfiguration.Models.NotificationType.Error));
|
MareConfiguration.Models.NotificationType.Error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
int removed = set.RemoveWhere(p => list.Contains(p, StringComparer.OrdinalIgnoreCase));
|
int removed = set.RemoveWhere(p => list.Contains(p, StringComparer.OrdinalIgnoreCase));
|
||||||
Logger.LogInformation("Removed {removed} previously existing transient paths", removed);
|
Logger.LogDebug("Removed {removed} previously existing transient paths", removed);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool reloadSemiTransient = false;
|
bool reloadSemiTransient = false;
|
||||||
@@ -222,7 +222,7 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
int removed = semiset.RemoveWhere(p => list.Contains(p, StringComparer.OrdinalIgnoreCase));
|
int removed = semiset.RemoveWhere(p => list.Contains(p, StringComparer.OrdinalIgnoreCase));
|
||||||
Logger.LogInformation("Removed {removed} previously existing semi transient paths", removed);
|
Logger.LogDebug("Removed {removed} previously existing semi transient paths", removed);
|
||||||
if (removed > 0)
|
if (removed > 0)
|
||||||
{
|
{
|
||||||
reloadSemiTransient = true;
|
reloadSemiTransient = true;
|
||||||
|
|||||||
106
MareSynchronos/Interop/ColorTableHook.cs
Normal file
106
MareSynchronos/Interop/ColorTableHook.cs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
using Dalamud.Hooking;
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
using Dalamud.Utility.Signatures;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||||
|
using MareSynchronos.MareConfiguration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Core;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
|
namespace MareSynchronos.Interop;
|
||||||
|
|
||||||
|
public unsafe sealed class ColorTableHook : IDisposable
|
||||||
|
{
|
||||||
|
// Based on https://github.com/Exter-N/MemShield/blob/main/MemShield/Plugin.cs
|
||||||
|
|
||||||
|
private readonly ILogger<ColorTableHook> _logger;
|
||||||
|
private const int ColorTableSize = 2048;
|
||||||
|
private const int ColorDyeTableSize = 128;
|
||||||
|
private readonly MareConfigService _configService;
|
||||||
|
|
||||||
|
#region signatures
|
||||||
|
#pragma warning disable CS0649
|
||||||
|
[Signature("E8 ?? ?? ?? ?? 49 89 04 3E",
|
||||||
|
DetourName = nameof(PrepareColorTableDetour))]
|
||||||
|
private Hook<MaterialResourceHandle.Delegates.PrepareColorTable> _prepareColorTable = null!;
|
||||||
|
#pragma warning restore CS0649
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public ColorTableHook(ILogger<ColorTableHook> logger, IGameInteropProvider gameInteropProvider, MareConfigService configService)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_configService = configService;
|
||||||
|
|
||||||
|
logger.LogInformation("Initializing ColorTableHook");
|
||||||
|
gameInteropProvider.InitializeFromAttributes(this);
|
||||||
|
|
||||||
|
if (_configService.Current.AttemptColorTableProtection)
|
||||||
|
{
|
||||||
|
_prepareColorTable.Enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_configService.Current.AttemptColorTableProtection)
|
||||||
|
{
|
||||||
|
_prepareColorTable.Disable();
|
||||||
|
}
|
||||||
|
_prepareColorTable.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnableHooks()
|
||||||
|
{
|
||||||
|
_prepareColorTable.Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisableHooks()
|
||||||
|
{
|
||||||
|
_prepareColorTable.Disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetDataSetExpectedSize(uint dataFlags)
|
||||||
|
=> (dataFlags & 16) != 0
|
||||||
|
? ColorTableSize + ((dataFlags & 8) != 0 ? ColorDyeTableSize : 0)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
private Texture* PrepareColorTableDetour(MaterialResourceHandle* @this, byte stain0Id, byte stain1Id)
|
||||||
|
{
|
||||||
|
if(!_configService.Current.AttemptColorTableProtection)
|
||||||
|
{
|
||||||
|
return _prepareColorTable.Original(@this, stain0Id, stain1Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedSize = GetDataSetExpectedSize(@this->DataFlags);
|
||||||
|
if (@this->DataSetSize >= expectedSize)
|
||||||
|
return _prepareColorTable.Original(@this, stain0Id, stain1Id);
|
||||||
|
var originalDataSetPtr = @this->DataSet;
|
||||||
|
var originalDataSet = new ReadOnlySpan<byte>(originalDataSetPtr, @this->DataSetSize);
|
||||||
|
var pool = ArrayPool<byte>.Shared;
|
||||||
|
var buffer = pool.Rent(expectedSize);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var bufferSpan = buffer.AsSpan(..expectedSize);
|
||||||
|
originalDataSet.CopyTo(bufferSpan);
|
||||||
|
bufferSpan[@this->DataSetSize..].Clear();
|
||||||
|
fixed (byte* bufferPtr = buffer)
|
||||||
|
{
|
||||||
|
@this->DataSet = bufferPtr;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _prepareColorTable.Original(@this, stain0Id, stain1Id);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
@this->DataSet = originalDataSetPtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
pool.Return(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -109,7 +109,7 @@ public sealed class IpcCallerGlamourer : DisposableMediatorSubscriberBase, IIpcC
|
|||||||
if (!apiAvailable && !_shownGlamourerUnavailable)
|
if (!apiAvailable && !_shownGlamourerUnavailable)
|
||||||
{
|
{
|
||||||
_shownGlamourerUnavailable = true;
|
_shownGlamourerUnavailable = true;
|
||||||
_mareMediator.Publish(new NotificationMessage("Glamourer inactive", "Your Glamourer installation is not active or out of date. Update Glamourer to continue to use Loporrit. If you just updated Glamourer, ignore this message.",
|
_mareMediator.Publish(new NotificationMessage("Glamourer inactive", "Your Glamourer installation is not active or out of date. Update Glamourer to continue to use Club Penguin Sync. If you just updated Glamourer, ignore this message.",
|
||||||
NotificationType.Error));
|
NotificationType.Error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
using Dalamud.Plugin;
|
|
||||||
using Dalamud.Plugin.Ipc;
|
|
||||||
using MareSynchronos.Services;
|
|
||||||
using MareSynchronos.Services.Mediator;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace MareSynchronos.Interop.Ipc;
|
|
||||||
|
|
||||||
public sealed class IpcCallerMare : DisposableMediatorSubscriberBase
|
|
||||||
{
|
|
||||||
private readonly ICallGateSubscriber<List<nint>> _mareHandledGameAddresses;
|
|
||||||
private readonly List<nint> _emptyList = [];
|
|
||||||
|
|
||||||
private bool _pluginLoaded;
|
|
||||||
|
|
||||||
public IpcCallerMare(ILogger<IpcCallerMare> logger, IDalamudPluginInterface pi, MareMediator mediator) : base(logger, mediator)
|
|
||||||
{
|
|
||||||
_mareHandledGameAddresses = pi.GetIpcSubscriber<List<nint>>("MareSynchronos.GetHandledAddresses");
|
|
||||||
|
|
||||||
_pluginLoaded = PluginWatcherService.GetInitialPluginState(pi, "MareSynchronos")?.IsLoaded ?? false;
|
|
||||||
|
|
||||||
Mediator.SubscribeKeyed<PluginChangeMessage>(this, "MareSynchronos", (msg) =>
|
|
||||||
{
|
|
||||||
_pluginLoaded = msg.IsLoaded;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool APIAvailable { get; private set; } = false;
|
|
||||||
|
|
||||||
// Must be called on framework thread
|
|
||||||
public IReadOnlyList<nint> GetHandledGameAddresses()
|
|
||||||
{
|
|
||||||
if (!_pluginLoaded) return _emptyList;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return _mareHandledGameAddresses.InvokeFunc();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return _emptyList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,9 +27,9 @@ public sealed class IpcCallerMoodles : IIpcCaller
|
|||||||
|
|
||||||
_moodlesApiVersion = pi.GetIpcSubscriber<int>("Moodles.Version");
|
_moodlesApiVersion = pi.GetIpcSubscriber<int>("Moodles.Version");
|
||||||
_moodlesOnChange = pi.GetIpcSubscriber<IPlayerCharacter, object>("Moodles.StatusManagerModified");
|
_moodlesOnChange = pi.GetIpcSubscriber<IPlayerCharacter, object>("Moodles.StatusManagerModified");
|
||||||
_moodlesGetStatus = pi.GetIpcSubscriber<nint, string>("Moodles.GetStatusManagerByPtr");
|
_moodlesGetStatus = pi.GetIpcSubscriber<nint, string>("Moodles.GetStatusManagerByPtrV2");
|
||||||
_moodlesSetStatus = pi.GetIpcSubscriber<nint, string, object>("Moodles.SetStatusManagerByPtr");
|
_moodlesSetStatus = pi.GetIpcSubscriber<nint, string, object>("Moodles.SetStatusManagerByPtrV2");
|
||||||
_moodlesRevertStatus = pi.GetIpcSubscriber<nint, object>("Moodles.ClearStatusManagerByPtr");
|
_moodlesRevertStatus = pi.GetIpcSubscriber<nint, object>("Moodles.ClearStatusManagerByPtrV2");
|
||||||
|
|
||||||
_moodlesOnChange.Subscribe(OnMoodlesChange);
|
_moodlesOnChange.Subscribe(OnMoodlesChange);
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ public sealed class IpcCallerMoodles : IIpcCaller
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
APIAvailable = _moodlesApiVersion.InvokeFunc() == 1;
|
APIAvailable = _moodlesApiVersion.InvokeFunc() == 3;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|||||||
83
MareSynchronos/Interop/Ipc/IpcCallerOtherSync.cs
Normal file
83
MareSynchronos/Interop/Ipc/IpcCallerOtherSync.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
using Dalamud.Plugin;
|
||||||
|
using Dalamud.Plugin.Ipc;
|
||||||
|
using MareSynchronos.Services;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MareSynchronos.Interop.Ipc;
|
||||||
|
|
||||||
|
public sealed class IpcCallerOtherSync : DisposableMediatorSubscriberBase
|
||||||
|
{
|
||||||
|
private readonly ICallGateSubscriber<List<nint>> _lightlessHandledGameAddresses;
|
||||||
|
private readonly ICallGateSubscriber<List<nint>> _snowcloakHandledGameAddresses;
|
||||||
|
|
||||||
|
private readonly List<nint> _emptyList = [];
|
||||||
|
|
||||||
|
private bool _lightlessLoaded;
|
||||||
|
private bool _snowcloakLoaded;
|
||||||
|
|
||||||
|
public IpcCallerOtherSync(ILogger<IpcCallerOtherSync> logger, IDalamudPluginInterface pi, MareMediator mediator) : base(logger, mediator)
|
||||||
|
{
|
||||||
|
_lightlessHandledGameAddresses = pi.GetIpcSubscriber<List<nint>>("LightlessSync.GetHandledAddresses");
|
||||||
|
_snowcloakHandledGameAddresses = pi.GetIpcSubscriber<List<nint>>("SnowcloakSync.GetHandledAddresses");
|
||||||
|
|
||||||
|
_lightlessLoaded = PluginWatcherService.GetInitialPluginState(pi, "LightlessSync")?.IsLoaded ?? false;
|
||||||
|
|
||||||
|
Mediator.SubscribeKeyed<PluginChangeMessage>(this, "LightlessSync", (msg) =>
|
||||||
|
{
|
||||||
|
_lightlessLoaded = msg.IsLoaded;
|
||||||
|
});
|
||||||
|
|
||||||
|
_snowcloakLoaded = PluginWatcherService.GetInitialPluginState(pi, "Snowcloak")?.IsLoaded ?? false;
|
||||||
|
|
||||||
|
Mediator.SubscribeKeyed<PluginChangeMessage>(this, "Snowcloak", (msg) =>
|
||||||
|
{
|
||||||
|
_snowcloakLoaded = msg.IsLoaded;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool APIAvailable { get; private set; } = false;
|
||||||
|
|
||||||
|
// Must be called on framework thread
|
||||||
|
public IReadOnlyList<nint> GetHandledGameAddresses()
|
||||||
|
{
|
||||||
|
if (!_lightlessLoaded && !_snowcloakLoaded) return _emptyList;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return GetLightlessHandledGameAddresses().Concat(GetSnowcloakHandledGameAddresses()).ToList();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return _emptyList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<nint> GetLightlessHandledGameAddresses()
|
||||||
|
{
|
||||||
|
if (!_lightlessLoaded) return _emptyList;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _lightlessHandledGameAddresses.InvokeFunc();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return _emptyList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<nint> GetSnowcloakHandledGameAddresses()
|
||||||
|
{
|
||||||
|
if (!_snowcloakLoaded) return _emptyList;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _snowcloakHandledGameAddresses.InvokeFunc();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return _emptyList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ using Penumbra.Api.Enums;
|
|||||||
using Penumbra.Api.Helpers;
|
using Penumbra.Api.Helpers;
|
||||||
using Penumbra.Api.IpcSubscribers;
|
using Penumbra.Api.IpcSubscribers;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Security.Principal;
|
||||||
|
|
||||||
namespace MareSynchronos.Interop.Ipc;
|
namespace MareSynchronos.Interop.Ipc;
|
||||||
|
|
||||||
@@ -114,7 +115,7 @@ public sealed class IpcCallerPenumbra : DisposableMediatorSubscriberBase, IIpcCa
|
|||||||
bool penumbraAvailable = false;
|
bool penumbraAvailable = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
penumbraAvailable = _pluginLoaded && _pluginVersion >= new Version(1, 0, 1, 0);
|
penumbraAvailable = _pluginLoaded && _pluginVersion >= new Version(1, 5, 1, 0);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
penumbraAvailable &= _penumbraEnabled.Invoke();
|
penumbraAvailable &= _penumbraEnabled.Invoke();
|
||||||
@@ -136,7 +137,7 @@ public sealed class IpcCallerPenumbra : DisposableMediatorSubscriberBase, IIpcCa
|
|||||||
{
|
{
|
||||||
_shownPenumbraUnavailable = true;
|
_shownPenumbraUnavailable = true;
|
||||||
_mareMediator.Publish(new NotificationMessage("Penumbra inactive",
|
_mareMediator.Publish(new NotificationMessage("Penumbra inactive",
|
||||||
"Your Penumbra installation is not active or out of date. Update Penumbra and/or the Enable Mods setting in Penumbra to continue to use Loporrit. If you just updated Penumbra, ignore this message.",
|
"Your Penumbra installation is not active or out of date. Update Penumbra and/or the Enable Mods setting in Penumbra to continue to use Club Penguin Sync. If you just updated Penumbra, ignore this message.",
|
||||||
NotificationType.Error));
|
NotificationType.Error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,14 +220,29 @@ public sealed class IpcCallerPenumbra : DisposableMediatorSubscriberBase, IIpcCa
|
|||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Random rng = new();
|
||||||
|
|
||||||
public async Task<Guid> CreateTemporaryCollectionAsync(ILogger logger, string uid)
|
public async Task<Guid> CreateTemporaryCollectionAsync(ILogger logger, string uid)
|
||||||
{
|
{
|
||||||
if (!APIAvailable) return Guid.Empty;
|
if (!APIAvailable) return Guid.Empty;
|
||||||
|
|
||||||
|
var identity = "ClubPenguin";
|
||||||
|
|
||||||
return await _dalamudUtil.RunOnFrameworkThread(() =>
|
return await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||||
{
|
{
|
||||||
var collName = "Loporrit_" + uid;
|
var collName = identity + "_" + uid;
|
||||||
var collId = _penumbraCreateNamedTemporaryCollection.Invoke(collName);
|
PenumbraApiEc ec = _penumbraCreateNamedTemporaryCollection.Invoke(identity, collName, out var collId);
|
||||||
|
if (ec == PenumbraApiEc.InvalidCredentials)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
var tmp = Guid.NewGuid().ToString().Replace("-", "").Substring(0, 8 + rng.Next() % 6);
|
||||||
|
logger.LogWarning("Identity = {identity}", identity);
|
||||||
|
ec = _penumbraCreateNamedTemporaryCollection.Invoke(tmp, collName, out collId);
|
||||||
|
if (ec != PenumbraApiEc.InvalidCredentials)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
logger.LogTrace("Creating Temp Collection {collName}, GUID: {collId}", collName, collId);
|
logger.LogTrace("Creating Temp Collection {collName}, GUID: {collId}", collName, collId);
|
||||||
return collId;
|
return collId;
|
||||||
|
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ public sealed class IpcCallerPetNames : IIpcCaller
|
|||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
_mareMediator = mareMediator;
|
_mareMediator = mareMediator;
|
||||||
|
|
||||||
_petnamesReady = pi.GetIpcSubscriber<object>("PetRenamer.Ready");
|
_petnamesReady = pi.GetIpcSubscriber<object>("PetRenamer.OnReady");
|
||||||
_petnamesDisposing = pi.GetIpcSubscriber<object>("PetRenamer.Disposing");
|
_petnamesDisposing = pi.GetIpcSubscriber<object>("PetRenamer.OnDisposing");
|
||||||
_apiVersion = pi.GetIpcSubscriber<(uint, uint)>("PetRenamer.ApiVersion");
|
_apiVersion = pi.GetIpcSubscriber<(uint, uint)>("PetRenamer.ApiVersion");
|
||||||
_enabled = pi.GetIpcSubscriber<bool>("PetRenamer.Enabled");
|
_enabled = pi.GetIpcSubscriber<bool>("PetRenamer.IsEnabled");
|
||||||
|
|
||||||
_playerDataChanged = pi.GetIpcSubscriber<string, object>("PetRenamer.PlayerDataChanged");
|
_playerDataChanged = pi.GetIpcSubscriber<string, object>("PetRenamer.OnPlayerDataChanged");
|
||||||
_getPlayerData = pi.GetIpcSubscriber<string>("PetRenamer.GetPlayerData");
|
_getPlayerData = pi.GetIpcSubscriber<string>("PetRenamer.GetPlayerData");
|
||||||
_setPlayerData = pi.GetIpcSubscriber<string, object>("PetRenamer.SetPlayerData");
|
_setPlayerData = pi.GetIpcSubscriber<string, object>("PetRenamer.SetPlayerData");
|
||||||
_clearPlayerData = pi.GetIpcSubscriber<ushort, object>("PetRenamer.ClearPlayerData");
|
_clearPlayerData = pi.GetIpcSubscriber<ushort, object>("PetRenamer.ClearPlayerData");
|
||||||
@@ -56,7 +56,7 @@ public sealed class IpcCallerPetNames : IIpcCaller
|
|||||||
APIAvailable = _enabled?.InvokeFunc() ?? false;
|
APIAvailable = _enabled?.InvokeFunc() ?? false;
|
||||||
if (APIAvailable)
|
if (APIAvailable)
|
||||||
{
|
{
|
||||||
APIAvailable = _apiVersion?.InvokeFunc() is { Item1: 3, Item2: >= 1 };
|
APIAvailable = _apiVersion?.InvokeFunc() is { Item1: 4, Item2: >= 0 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using MareSynchronos.MareConfiguration;
|
|||||||
using MareSynchronos.PlayerData.Handlers;
|
using MareSynchronos.PlayerData.Handlers;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Utils;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
@@ -15,25 +14,12 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
|
|||||||
{
|
{
|
||||||
private readonly ILogger<IpcProvider> _logger;
|
private readonly ILogger<IpcProvider> _logger;
|
||||||
private readonly IDalamudPluginInterface _pi;
|
private readonly IDalamudPluginInterface _pi;
|
||||||
private readonly MareConfigService _mareConfig;
|
|
||||||
private readonly CharaDataManager _charaDataManager;
|
private readonly CharaDataManager _charaDataManager;
|
||||||
private ICallGateProvider<string, IGameObject, bool>? _loadFileProvider;
|
private ICallGateProvider<string, IGameObject, bool>? _loadFileProvider;
|
||||||
private ICallGateProvider<string, IGameObject, Task<bool>>? _loadFileAsyncProvider;
|
private ICallGateProvider<string, IGameObject, Task<bool>>? _loadFileAsyncProvider;
|
||||||
private ICallGateProvider<List<nint>>? _handledGameAddresses;
|
private ICallGateProvider<List<nint>>? _handledGameAddresses;
|
||||||
private readonly List<GameObjectHandler> _activeGameObjectHandlers = [];
|
private readonly List<GameObjectHandler> _activeGameObjectHandlers = [];
|
||||||
|
|
||||||
private ICallGateProvider<string, IGameObject, bool>? _loadFileProviderMare;
|
|
||||||
private ICallGateProvider<string, IGameObject, Task<bool>>? _loadFileAsyncProviderMare;
|
|
||||||
private ICallGateProvider<List<nint>>? _handledGameAddressesMare;
|
|
||||||
|
|
||||||
private bool _marePluginEnabled = false;
|
|
||||||
private bool _impersonating = false;
|
|
||||||
private DateTime _unregisterTime = DateTime.UtcNow;
|
|
||||||
private CancellationTokenSource _registerDelayCts = new();
|
|
||||||
|
|
||||||
public bool MarePluginEnabled => _marePluginEnabled;
|
|
||||||
public bool ImpersonationActive => _impersonating;
|
|
||||||
|
|
||||||
public MareMediator Mediator { get; init; }
|
public MareMediator Mediator { get; init; }
|
||||||
|
|
||||||
public IpcProvider(ILogger<IpcProvider> logger, IDalamudPluginInterface pi, MareConfigService mareConfig,
|
public IpcProvider(ILogger<IpcProvider> logger, IDalamudPluginInterface pi, MareConfigService mareConfig,
|
||||||
@@ -41,7 +27,6 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
|
|||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_pi = pi;
|
_pi = pi;
|
||||||
_mareConfig = mareConfig;
|
|
||||||
_charaDataManager = charaDataManager;
|
_charaDataManager = charaDataManager;
|
||||||
Mediator = mareMediator;
|
Mediator = mareMediator;
|
||||||
|
|
||||||
@@ -55,90 +40,22 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
|
|||||||
if (msg.OwnedObject) return;
|
if (msg.OwnedObject) return;
|
||||||
_activeGameObjectHandlers.Remove(msg.GameObjectHandler);
|
_activeGameObjectHandlers.Remove(msg.GameObjectHandler);
|
||||||
});
|
});
|
||||||
|
|
||||||
_marePluginEnabled = PluginWatcherService.GetInitialPluginState(pi, "MareSynchronos")?.IsLoaded ?? false;
|
|
||||||
Mediator.SubscribeKeyed<PluginChangeMessage>(this, "MareSynchronos", p => {
|
|
||||||
_marePluginEnabled = p.IsLoaded;
|
|
||||||
HandleMareImpersonation(automatic: true);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Starting IpcProvider Service");
|
_logger.LogDebug("Starting IpcProvider Service");
|
||||||
_loadFileProvider = _pi.GetIpcProvider<string, IGameObject, bool>("LoporritSync.LoadMcdf");
|
_loadFileProvider = _pi.GetIpcProvider<string, IGameObject, bool>("ClubPenguinSync.LoadMcdf");
|
||||||
_loadFileProvider.RegisterFunc(LoadMcdf);
|
_loadFileProvider.RegisterFunc(LoadMcdf);
|
||||||
_loadFileAsyncProvider = _pi.GetIpcProvider<string, IGameObject, Task<bool>>("LoporritSync.LoadMcdfAsync");
|
_loadFileAsyncProvider = _pi.GetIpcProvider<string, IGameObject, Task<bool>>("ClubPenguinSync.LoadMcdfAsync");
|
||||||
_loadFileAsyncProvider.RegisterFunc(LoadMcdfAsync);
|
_loadFileAsyncProvider.RegisterFunc(LoadMcdfAsync);
|
||||||
_handledGameAddresses = _pi.GetIpcProvider<List<nint>>("LoporritSync.GetHandledAddresses");
|
_handledGameAddresses = _pi.GetIpcProvider<List<nint>>("ClubPenguinSync.GetHandledAddresses");
|
||||||
_handledGameAddresses.RegisterFunc(GetHandledAddresses);
|
_handledGameAddresses.RegisterFunc(GetHandledAddresses);
|
||||||
|
|
||||||
_loadFileProviderMare = _pi.GetIpcProvider<string, IGameObject, bool>("MareSynchronos.LoadMcdf");
|
|
||||||
_loadFileAsyncProviderMare = _pi.GetIpcProvider<string, IGameObject, Task<bool>>("MareSynchronos.LoadMcdfAsync");
|
|
||||||
_handledGameAddressesMare = _pi.GetIpcProvider<List<nint>>("MareSynchronos.GetHandledAddresses");
|
|
||||||
HandleMareImpersonation(automatic: true);
|
|
||||||
|
|
||||||
_logger.LogInformation("Started IpcProviderService");
|
_logger.LogInformation("Started IpcProviderService");
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleMareImpersonation(bool automatic = false)
|
|
||||||
{
|
|
||||||
if (_marePluginEnabled)
|
|
||||||
{
|
|
||||||
if (_impersonating)
|
|
||||||
{
|
|
||||||
_loadFileProviderMare?.UnregisterFunc();
|
|
||||||
_loadFileAsyncProviderMare?.UnregisterFunc();
|
|
||||||
_handledGameAddressesMare?.UnregisterFunc();
|
|
||||||
_impersonating = false;
|
|
||||||
_unregisterTime = DateTime.UtcNow;
|
|
||||||
_logger.LogDebug("Unregistered MareSynchronos API");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_mareConfig.Current.MareAPI)
|
|
||||||
{
|
|
||||||
var cancelToken = _registerDelayCts.Token;
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
// Wait before registering to reduce the chance of a race condition
|
|
||||||
if (automatic)
|
|
||||||
await Task.Delay(5000);
|
|
||||||
|
|
||||||
if (cancelToken.IsCancellationRequested)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_marePluginEnabled)
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Not registering MareSynchronos API: Mare plugin is loaded");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_loadFileProviderMare?.RegisterFunc(LoadMcdf);
|
|
||||||
_loadFileAsyncProviderMare?.RegisterFunc(LoadMcdfAsync);
|
|
||||||
_handledGameAddressesMare?.RegisterFunc(GetHandledAddresses);
|
|
||||||
_impersonating = true;
|
|
||||||
_logger.LogDebug("Registered MareSynchronos API");
|
|
||||||
}, cancelToken);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_registerDelayCts = _registerDelayCts.CancelRecreate();
|
|
||||||
if (_impersonating)
|
|
||||||
{
|
|
||||||
_loadFileProviderMare?.UnregisterFunc();
|
|
||||||
_loadFileAsyncProviderMare?.UnregisterFunc();
|
|
||||||
_handledGameAddressesMare?.UnregisterFunc();
|
|
||||||
_impersonating = false;
|
|
||||||
_unregisterTime = DateTime.UtcNow;
|
|
||||||
_logger.LogDebug("Unregistered MareSynchronos API");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken)
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Stopping IpcProvider Service");
|
_logger.LogDebug("Stopping IpcProvider Service");
|
||||||
@@ -146,14 +63,6 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
|
|||||||
_loadFileAsyncProvider?.UnregisterFunc();
|
_loadFileAsyncProvider?.UnregisterFunc();
|
||||||
_handledGameAddresses?.UnregisterFunc();
|
_handledGameAddresses?.UnregisterFunc();
|
||||||
|
|
||||||
_registerDelayCts.Cancel();
|
|
||||||
if (_impersonating)
|
|
||||||
{
|
|
||||||
_loadFileProviderMare?.UnregisterFunc();
|
|
||||||
_loadFileAsyncProviderMare?.UnregisterFunc();
|
|
||||||
_handledGameAddressesMare?.UnregisterFunc();
|
|
||||||
}
|
|
||||||
|
|
||||||
Mediator.UnsubscribeAll(this);
|
Mediator.UnsubscribeAll(this);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@@ -181,16 +90,6 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
|
|||||||
|
|
||||||
private List<nint> GetHandledAddresses()
|
private List<nint> GetHandledAddresses()
|
||||||
{
|
{
|
||||||
if (!_impersonating)
|
|
||||||
{
|
|
||||||
if ((DateTime.UtcNow - _unregisterTime).TotalSeconds >= 1.0)
|
|
||||||
{
|
|
||||||
_logger.LogWarning("GetHandledAddresses called when it should not be registered");
|
|
||||||
_handledGameAddressesMare?.UnregisterFunc();
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return _activeGameObjectHandlers.Where(g => g.Address != nint.Zero).Select(g => g.Address).Distinct().ToList();
|
return _activeGameObjectHandlers.Where(g => g.Address != nint.Zero).Select(g => g.Address).Distinct().ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"Author": "Huggingway",
|
|
||||||
"Name": "Loporrit Sync",
|
|
||||||
"Punchline": "Social modding for everyone!",
|
|
||||||
"Description": "This plugin will synchronize your Penumbra mods and current Glamourer state with other paired clients automatically.",
|
|
||||||
"InternalName": "LoporritSync",
|
|
||||||
"ApplicableVersion": "any",
|
|
||||||
"Tags": [
|
|
||||||
"customization"
|
|
||||||
],
|
|
||||||
"IconUrl": "https://raw.githubusercontent.com/loporrit/MareClient/way4/MareSynchronos/images/icon.png",
|
|
||||||
"RepoUrl": "https://github.com/loporrit/MareClient/",
|
|
||||||
"CanUnloadAsync": true
|
|
||||||
}
|
|
||||||
@@ -29,8 +29,10 @@ public class MareConfig : IMareConfiguration
|
|||||||
public bool InitialScanComplete { get; set; } = false;
|
public bool InitialScanComplete { get; set; } = false;
|
||||||
public LogLevel LogLevel { get; set; } = LogLevel.Information;
|
public LogLevel LogLevel { get; set; } = LogLevel.Information;
|
||||||
public bool LogPerformance { get; set; } = false;
|
public bool LogPerformance { get; set; } = false;
|
||||||
|
public bool LogTraceLog { get; set; } = false;
|
||||||
public bool LogEvents { get; set; } = true;
|
public bool LogEvents { get; set; } = true;
|
||||||
public bool HoldCombatApplication { get; set; } = false;
|
public bool HoldCombatApplication { get; set; } = true;
|
||||||
|
public bool AttemptColorTableProtection { get; set; } = true;
|
||||||
public double MaxLocalCacheInGiB { get; set; } = 20;
|
public double MaxLocalCacheInGiB { get; set; } = 20;
|
||||||
public bool OpenGposeImportOnGposeStart { get; set; } = false;
|
public bool OpenGposeImportOnGposeStart { get; set; } = false;
|
||||||
public bool OpenPopupOnAdd { get; set; } = true;
|
public bool OpenPopupOnAdd { get; set; } = true;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public class ServerConfig : IMareConfiguration
|
|||||||
|
|
||||||
public List<ServerStorage> ServerStorage { get; set; } = new()
|
public List<ServerStorage> ServerStorage { get; set; } = new()
|
||||||
{
|
{
|
||||||
{ new ServerStorage() { ServerName = ApiController.LoporritServer, ServerUri = ApiController.LoporritServiceUri } },
|
{ new ServerStorage() { ServerName = ApiController.ClubPenguinServer, ServerUri = ApiController.ClubPenguinServiceUri } },
|
||||||
};
|
};
|
||||||
|
|
||||||
public int Version { get; set; } = 1;
|
public int Version { get; set; } = 1;
|
||||||
|
|||||||
@@ -89,9 +89,9 @@ public class MarePlugin : MediatorSubscriberBase, IHostedService
|
|||||||
public Task StartAsync(CancellationToken cancellationToken)
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var version = Assembly.GetExecutingAssembly().GetName().Version!;
|
var version = Assembly.GetExecutingAssembly().GetName().Version!;
|
||||||
Logger.LogInformation("Launching {name} {major}.{minor}.{build}.{rev}", "Loporrit Sync", version.Major, version.Minor, version.Build, version.Revision);
|
Logger.LogInformation("Launching {name} {major}.{minor}.{build}.{rev}", "Club Penguin Sync", version.Major, version.Minor, version.Build, version.Revision);
|
||||||
Mediator.Publish(new EventMessage(new Services.Events.Event(nameof(MarePlugin), Services.Events.EventSeverity.Informational,
|
Mediator.Publish(new EventMessage(new Services.Events.Event(nameof(MarePlugin), Services.Events.EventSeverity.Informational,
|
||||||
$"Starting Loporrit Sync {version.Major}.{version.Minor}.{version.Build}.{version.Revision}")));
|
$"Starting Club Penguin Sync {version.Major}.{version.Minor}.{version.Build}.{version.Revision}")));
|
||||||
|
|
||||||
Mediator.Subscribe<SwitchToMainUiMessage>(this, (msg) => { if (_launchTask == null || _launchTask.IsCompleted) _launchTask = Task.Run(WaitForPlayerAndLaunchCharacterManager); });
|
Mediator.Subscribe<SwitchToMainUiMessage>(this, (msg) => { if (_launchTask == null || _launchTask.IsCompleted) _launchTask = Task.Run(WaitForPlayerAndLaunchCharacterManager); });
|
||||||
Mediator.Subscribe<DalamudLoginMessage>(this, (_) => DalamudUtilOnLogIn());
|
Mediator.Subscribe<DalamudLoginMessage>(this, (_) => DalamudUtilOnLogIn());
|
||||||
@@ -115,6 +115,7 @@ public class MarePlugin : MediatorSubscriberBase, IHostedService
|
|||||||
|
|
||||||
private void DalamudUtilOnLogIn()
|
private void DalamudUtilOnLogIn()
|
||||||
{
|
{
|
||||||
|
ClubPenguinSync.Plugin.Self.ToggleFileLogging(_mareConfigService.Current.LogTraceLog, "Login");
|
||||||
Logger?.LogDebug("Client login");
|
Logger?.LogDebug("Client login");
|
||||||
if (_launchTask == null || _launchTask.IsCompleted) _launchTask = Task.Run(WaitForPlayerAndLaunchCharacterManager);
|
if (_launchTask == null || _launchTask.IsCompleted) _launchTask = Task.Run(WaitForPlayerAndLaunchCharacterManager);
|
||||||
}
|
}
|
||||||
@@ -124,6 +125,7 @@ public class MarePlugin : MediatorSubscriberBase, IHostedService
|
|||||||
Logger?.LogDebug("Client logout");
|
Logger?.LogDebug("Client logout");
|
||||||
|
|
||||||
_runtimeServiceScope?.Dispose();
|
_runtimeServiceScope?.Dispose();
|
||||||
|
ClubPenguinSync.Plugin.Self.ToggleFileLogging(true, "Logout");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task WaitForPlayerAndLaunchCharacterManager()
|
private async Task WaitForPlayerAndLaunchCharacterManager()
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project Sdk="Dalamud.NET.Sdk/13.0.0">
|
<Project Sdk="Dalamud.NET.Sdk/13.1.0">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>LoporritSync</AssemblyName>
|
<AssemblyName>ClubPenguinSync</AssemblyName>
|
||||||
<Version>9.9.0</Version>
|
<Version>1.7.1.2</Version>
|
||||||
<PackageProjectUrl>https://github.com/loporrit/MareClient/</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/Rawrington/ClubPenguinSync/</PackageProjectUrl>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -31,22 +31,6 @@
|
|||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.7.0" />
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.7.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="Exists('..\Penumbra.Api\Penumbra.Api.csproj')">
|
|
||||||
<ProjectReference Include="..\Penumbra.Api\Penumbra.Api.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="!Exists('..\Penumbra.Api\Penumbra.Api.csproj')">
|
|
||||||
<PackageReference Include="Penumbra.Api" Version="5.6.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="Exists('..\Glamourer.Api\Glamourer.Api.csproj')">
|
|
||||||
<ProjectReference Include="..\Glamourer.Api\Glamourer.Api.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="!Exists('..\Glamourer.Api\Glamourer.Api.csproj')">
|
|
||||||
<PackageReference Include="Glamourer.Api" Version="2.4.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<SourceRevisionId>build$([System.DateTime]::UtcNow.ToString("yyyy-MM-ddTHH:mm:ss:fffZ"))</SourceRevisionId>
|
<SourceRevisionId>build$([System.DateTime]::UtcNow.ToString("yyyy-MM-ddTHH:mm:ss:fffZ"))</SourceRevisionId>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
@@ -55,6 +39,8 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\MareAPI\MareSynchronosAPI\MareSynchronos.API.csproj" />
|
<ProjectReference Include="..\MareAPI\MareSynchronosAPI\MareSynchronos.API.csproj" />
|
||||||
<ProjectReference Include="..\BunnyWhispers\Chaos.NaCl\Chaos.NaCl.csproj" />
|
<ProjectReference Include="..\BunnyWhispers\Chaos.NaCl\Chaos.NaCl.csproj" />
|
||||||
|
<ProjectReference Include="..\Penumbra.Api\Penumbra.Api.csproj" />
|
||||||
|
<ProjectReference Include="..\Glamourer.Api\Glamourer.Api.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -27,14 +27,13 @@ public class PairHandlerFactory
|
|||||||
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
||||||
private readonly PairAnalyzerFactory _pairAnalyzerFactory;
|
private readonly PairAnalyzerFactory _pairAnalyzerFactory;
|
||||||
private readonly VisibilityService _visibilityService;
|
private readonly VisibilityService _visibilityService;
|
||||||
private readonly NoSnapService _noSnapService;
|
|
||||||
|
|
||||||
public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager,
|
public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager,
|
||||||
FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService,
|
FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService,
|
||||||
PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime,
|
PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime,
|
||||||
FileCacheManager fileCacheManager, MareMediator mareMediator, PlayerPerformanceService playerPerformanceService,
|
FileCacheManager fileCacheManager, MareMediator mareMediator, PlayerPerformanceService playerPerformanceService,
|
||||||
ServerConfigurationManager serverConfigManager, PairAnalyzerFactory pairAnalyzerFactory,
|
ServerConfigurationManager serverConfigManager, PairAnalyzerFactory pairAnalyzerFactory,
|
||||||
MareConfigService configService, VisibilityService visibilityService, NoSnapService noSnapService)
|
MareConfigService configService, VisibilityService visibilityService)
|
||||||
{
|
{
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
||||||
@@ -50,13 +49,12 @@ public class PairHandlerFactory
|
|||||||
_pairAnalyzerFactory = pairAnalyzerFactory;
|
_pairAnalyzerFactory = pairAnalyzerFactory;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_visibilityService = visibilityService;
|
_visibilityService = visibilityService;
|
||||||
_noSnapService = noSnapService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PairHandler Create(Pair pair)
|
public PairHandler Create(Pair pair)
|
||||||
{
|
{
|
||||||
return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), pair, _pairAnalyzerFactory.Create(pair), _gameObjectHandlerFactory,
|
return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), pair, _pairAnalyzerFactory.Create(pair), _gameObjectHandlerFactory,
|
||||||
_ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime,
|
_ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime,
|
||||||
_fileCacheManager, _mareMediator, _playerPerformanceService, _serverConfigManager, _configService, _visibilityService, _noSnapService);
|
_fileCacheManager, _mareMediator, _playerPerformanceService, _serverConfigManager, _configService, _visibilityService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -239,12 +239,19 @@ public class PlayerDataFactory
|
|||||||
_logger.LogDebug("Removed {amount} of invalid files", removed);
|
_logger.LogDebug("Removed {amount} of invalid files", removed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ct.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
if (objectKind == ObjectKind.Player)
|
if (objectKind == ObjectKind.Player)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await VerifyPlayerAnimationBones(boneIndices, (fragment as CharacterDataFragmentPlayer)!, ct).ConfigureAwait(false);
|
await VerifyPlayerAnimationBones(boneIndices, (fragment as CharacterDataFragmentPlayer)!, ct).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException e)
|
||||||
|
{
|
||||||
|
_logger.LogDebug(e, "Cancelled during player animation verification");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(e, "Failed to verify player animations, continuing without further verification");
|
_logger.LogWarning(e, "Failed to verify player animations, continuing without further verification");
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
private readonly ServerConfigurationManager _serverConfigManager;
|
private readonly ServerConfigurationManager _serverConfigManager;
|
||||||
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
||||||
private readonly VisibilityService _visibilityService;
|
private readonly VisibilityService _visibilityService;
|
||||||
private readonly NoSnapService _noSnapService;
|
|
||||||
private CancellationTokenSource? _applicationCancellationTokenSource = new();
|
private CancellationTokenSource? _applicationCancellationTokenSource = new();
|
||||||
private Guid _applicationId;
|
private Guid _applicationId;
|
||||||
private Task? _applicationTask;
|
private Task? _applicationTask;
|
||||||
@@ -55,8 +54,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
FileCacheManager fileDbManager, MareMediator mediator,
|
FileCacheManager fileDbManager, MareMediator mediator,
|
||||||
PlayerPerformanceService playerPerformanceService,
|
PlayerPerformanceService playerPerformanceService,
|
||||||
ServerConfigurationManager serverConfigManager,
|
ServerConfigurationManager serverConfigManager,
|
||||||
MareConfigService configService, VisibilityService visibilityService,
|
MareConfigService configService, VisibilityService visibilityService) : base(logger, mediator)
|
||||||
NoSnapService noSnapService) : base(logger, mediator)
|
|
||||||
{
|
{
|
||||||
Pair = pair;
|
Pair = pair;
|
||||||
PairAnalyzer = pairAnalyzer;
|
PairAnalyzer = pairAnalyzer;
|
||||||
@@ -70,7 +68,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
_serverConfigManager = serverConfigManager;
|
_serverConfigManager = serverConfigManager;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_visibilityService = visibilityService;
|
_visibilityService = visibilityService;
|
||||||
_noSnapService = noSnapService;
|
|
||||||
|
|
||||||
_visibilityService.StartTracking(Pair.Ident);
|
_visibilityService.StartTracking(Pair.Ident);
|
||||||
|
|
||||||
@@ -332,7 +329,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
var gposeName = actor.Name.TextValue;
|
var gposeName = actor.Name.TextValue;
|
||||||
if (!name.Equals(gposeName, StringComparison.Ordinal))
|
if (!name.Equals(gposeName, StringComparison.Ordinal))
|
||||||
continue;
|
continue;
|
||||||
_noSnapService.AddGposer(actor.ObjectIndex);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -385,10 +381,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_dalamudUtil.IsInCutscene && !string.IsNullOrEmpty(name))
|
|
||||||
{
|
|
||||||
_noSnapService.AddGposerNamed(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -725,14 +717,16 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
if (!IsVisible && nowVisible)
|
if (!IsVisible && nowVisible)
|
||||||
{
|
{
|
||||||
// This is deferred application attempt, avoid any log output
|
// This is a deferred application attempt, avoid any repeat log output
|
||||||
if (_deferred != Guid.Empty)
|
if (_deferred != Guid.Empty)
|
||||||
{
|
{
|
||||||
_isVisible = true;
|
_isVisible = true;
|
||||||
|
Mediator.Publish(new PairHandlerVisibleMessage(this));
|
||||||
_ = Task.Run(() =>
|
_ = Task.Run(() =>
|
||||||
{
|
{
|
||||||
ApplyCharacterData(_deferred, _cachedData!, forceApplyCustomization: true);
|
ApplyCharacterData(_deferred, _cachedData!, forceApplyCustomization: true);
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IsVisible = true;
|
IsVisible = true;
|
||||||
|
|||||||
@@ -76,6 +76,53 @@ public class Pair : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
private PairHandler? CachedPlayer { get; set; }
|
private PairHandler? CachedPlayer { get; set; }
|
||||||
|
|
||||||
|
public bool IsWhitelisted
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _serverConfigurationManager.IsUidWhitelisted(UserData.UID);
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
_serverConfigurationManager.AddWhitelistUid(UserData.UID);
|
||||||
|
UnholdApplication("Blacklist", skipApplication: true);
|
||||||
|
ApplyLastReceivedData(forced: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_serverConfigurationManager.RemoveWhitelistUid(UserData.UID);
|
||||||
|
ApplyLastReceivedData(forced: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsBlacklisted
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _serverConfigurationManager.IsUidBlacklisted(UserData.UID);
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
_serverConfigurationManager.AddBlacklistUid(UserData.UID);
|
||||||
|
HoldApplication("Blacklist", maxValue: 1);
|
||||||
|
ApplyLastReceivedData(forced: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_serverConfigurationManager.RemoveBlacklistUid(UserData.UID);
|
||||||
|
UnholdApplication("Blacklist", skipApplication: true);
|
||||||
|
ApplyLastReceivedData(forced: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void AddContextMenu(IMenuOpenedArgs args)
|
public void AddContextMenu(IMenuOpenedArgs args)
|
||||||
{
|
{
|
||||||
if (CachedPlayer == null || (args.Target is not MenuTargetDefault target) || target.TargetObjectId != CachedPlayer.PlayerCharacterId || IsPaused) return;
|
if (CachedPlayer == null || (args.Target is not MenuTargetDefault target) || target.TargetObjectId != CachedPlayer.PlayerCharacterId || IsPaused) return;
|
||||||
@@ -86,40 +133,37 @@ public class Pair : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
Name = name,
|
Name = name,
|
||||||
OnClicked = action,
|
OnClicked = action,
|
||||||
PrefixColor = 559,
|
PrefixColor = 555,
|
||||||
PrefixChar = 'L'
|
PrefixChar = 'C'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isBlocked = IsApplicationBlocked;
|
bool isBlocked = IsApplicationBlocked;
|
||||||
bool isBlacklisted = _serverConfigurationManager.IsUidBlacklisted(UserData.UID);
|
bool isBlacklisted = IsBlacklisted;
|
||||||
bool isWhitelisted = _serverConfigurationManager.IsUidWhitelisted(UserData.UID);
|
bool isWhitelisted = IsWhitelisted;
|
||||||
|
|
||||||
Add("Open Profile", _ => Mediator.Publish(new ProfileOpenStandaloneMessage(this)));
|
Add("Open Profile", _ => Mediator.Publish(new ProfileOpenStandaloneMessage(this)));
|
||||||
|
|
||||||
if (!isBlocked && !isBlacklisted)
|
if (!isBlocked && !isBlacklisted)
|
||||||
Add("Always Block Modded Appearance", _ => {
|
Add("Always Block Modded Appearance", _ =>
|
||||||
_serverConfigurationManager.AddBlacklistUid(UserData.UID);
|
{
|
||||||
HoldApplication("Blacklist", maxValue: 1);
|
IsBlacklisted = true;
|
||||||
ApplyLastReceivedData(forced: true);
|
|
||||||
});
|
});
|
||||||
else if (isBlocked && !isWhitelisted)
|
else if (isBlocked && !isWhitelisted)
|
||||||
Add("Always Allow Modded Appearance", _ => {
|
Add("Always Allow Modded Appearance", _ =>
|
||||||
_serverConfigurationManager.AddWhitelistUid(UserData.UID);
|
{
|
||||||
UnholdApplication("Blacklist", skipApplication: true);
|
IsWhitelisted = true;
|
||||||
ApplyLastReceivedData(forced: true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isWhitelisted)
|
if (isWhitelisted)
|
||||||
Add("Remove from Whitelist", _ => {
|
Add("Remove from Whitelist", _ =>
|
||||||
_serverConfigurationManager.RemoveWhitelistUid(UserData.UID);
|
{
|
||||||
ApplyLastReceivedData(forced: true);
|
IsWhitelisted = false;
|
||||||
});
|
});
|
||||||
else if (isBlacklisted)
|
else if (isBlacklisted)
|
||||||
Add("Remove from Blacklist", _ => {
|
Add("Remove from Blacklist", _ =>
|
||||||
_serverConfigurationManager.RemoveBlacklistUid(UserData.UID);
|
{
|
||||||
UnholdApplication("Blacklist", skipApplication: true);
|
IsBlacklisted = false;
|
||||||
ApplyLastReceivedData(forced: true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Add("Reapply last data", _ => ApplyLastReceivedData(forced: true));
|
Add("Reapply last data", _ => ApplyLastReceivedData(forced: true));
|
||||||
@@ -171,11 +215,6 @@ public class Pair : DisposableMediatorSubscriberBase
|
|||||||
if (_serverConfigurationManager.IsUidBlacklisted(UserData.UID))
|
if (_serverConfigurationManager.IsUidBlacklisted(UserData.UID))
|
||||||
HoldApplication("Blacklist", maxValue: 1);
|
HoldApplication("Blacklist", maxValue: 1);
|
||||||
|
|
||||||
if (NoSnapService.AnyLoaded)
|
|
||||||
HoldApplication("NoSnap", maxValue: 1);
|
|
||||||
else
|
|
||||||
UnholdApplication("NoSnap", skipApplication: true);
|
|
||||||
|
|
||||||
CachedPlayer.ApplyCharacterData(Guid.NewGuid(), RemoveNotSyncedFiles(LastReceivedCharacterData.DeepClone())!, forced);
|
CachedPlayer.ApplyCharacterData(Guid.NewGuid(), RemoveNotSyncedFiles(LastReceivedCharacterData.DeepClone())!, forced);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,18 +29,19 @@ using NReco.Logging.File;
|
|||||||
|
|
||||||
using MareSynchronos;
|
using MareSynchronos;
|
||||||
|
|
||||||
namespace LoporritSync;
|
namespace ClubPenguinSync;
|
||||||
|
|
||||||
public sealed class Plugin : IDalamudPlugin
|
public sealed class Plugin : IDalamudPlugin
|
||||||
{
|
{
|
||||||
private readonly IHost _host;
|
private readonly IHost _host;
|
||||||
|
private bool _traceLogEnabled = true;
|
||||||
|
|
||||||
#pragma warning disable CA2211, CS8618, MA0069, S1104, S2223
|
#pragma warning disable CA2211, CS8618, MA0069, S1104, S2223
|
||||||
public static Plugin Self;
|
public static Plugin Self;
|
||||||
#pragma warning restore CA2211, CS8618, MA0069, S1104, S2223
|
#pragma warning restore CA2211, CS8618, MA0069, S1104, S2223
|
||||||
public Action<IFramework>? RealOnFrameworkUpdate { get; set; }
|
public Action<IFramework>? RealOnFrameworkUpdate { get; set; }
|
||||||
|
|
||||||
// Proxy function in the LoporritSync namespace to avoid confusion in /xlstats
|
// Proxy function in the ClubPenguinSync namespace to avoid confusion in /xlstats
|
||||||
public void OnFrameworkUpdate(IFramework framework)
|
public void OnFrameworkUpdate(IFramework framework)
|
||||||
{
|
{
|
||||||
RealOnFrameworkUpdate?.Invoke(framework);
|
RealOnFrameworkUpdate?.Invoke(framework);
|
||||||
@@ -87,7 +88,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
{
|
{
|
||||||
lb.ClearProviders();
|
lb.ClearProviders();
|
||||||
lb.AddDalamudLogging(pluginLog);
|
lb.AddDalamudLogging(pluginLog);
|
||||||
lb.AddFile(Path.Combine(traceDir, $"mare-trace-{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.log"), (opt) =>
|
lb.AddFile(Path.Combine(traceDir, $"trace-{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.log"), (opt) =>
|
||||||
{
|
{
|
||||||
opt.Append = true;
|
opt.Append = true;
|
||||||
opt.RollingFilesConvention = FileLoggerOptions.FileRollingConvention.Ascending;
|
opt.RollingFilesConvention = FileLoggerOptions.FileRollingConvention.Ascending;
|
||||||
@@ -95,10 +96,11 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
opt.FileSizeLimitBytes = 50 * 1024 * 1024;
|
opt.FileSizeLimitBytes = 50 * 1024 * 1024;
|
||||||
});
|
});
|
||||||
lb.SetMinimumLevel(LogLevel.Trace);
|
lb.SetMinimumLevel(LogLevel.Trace);
|
||||||
|
lb.AddFilter<FileLoggerProvider>(_ => _traceLogEnabled);
|
||||||
})
|
})
|
||||||
.ConfigureServices(collection =>
|
.ConfigureServices(collection =>
|
||||||
{
|
{
|
||||||
collection.AddSingleton(new WindowSystem("LoporritSync"));
|
collection.AddSingleton(new WindowSystem("ClubPenguinSync"));
|
||||||
collection.AddSingleton<FileDialogManager>();
|
collection.AddSingleton<FileDialogManager>();
|
||||||
|
|
||||||
// add dalamud services
|
// add dalamud services
|
||||||
@@ -159,10 +161,10 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddSingleton<CharaDataGposeTogetherManager>();
|
collection.AddSingleton<CharaDataGposeTogetherManager>();
|
||||||
|
|
||||||
collection.AddSingleton<VfxSpawnManager>();
|
collection.AddSingleton<VfxSpawnManager>();
|
||||||
|
collection.AddSingleton<ColorTableHook>();
|
||||||
collection.AddSingleton<BlockedCharacterHandler>();
|
collection.AddSingleton<BlockedCharacterHandler>();
|
||||||
collection.AddSingleton<IpcProvider>();
|
collection.AddSingleton<IpcProvider>();
|
||||||
collection.AddSingleton<VisibilityService>();
|
collection.AddSingleton<VisibilityService>();
|
||||||
collection.AddSingleton<RepoChangeService>();
|
|
||||||
collection.AddSingleton<EventAggregator>();
|
collection.AddSingleton<EventAggregator>();
|
||||||
collection.AddSingleton<DalamudUtilService>();
|
collection.AddSingleton<DalamudUtilService>();
|
||||||
collection.AddSingleton<DtrEntry>();
|
collection.AddSingleton<DtrEntry>();
|
||||||
@@ -176,10 +178,9 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddSingleton<IpcCallerMoodles>();
|
collection.AddSingleton<IpcCallerMoodles>();
|
||||||
collection.AddSingleton<IpcCallerPetNames>();
|
collection.AddSingleton<IpcCallerPetNames>();
|
||||||
collection.AddSingleton<IpcCallerBrio>();
|
collection.AddSingleton<IpcCallerBrio>();
|
||||||
collection.AddSingleton<IpcCallerMare>();
|
collection.AddSingleton<IpcCallerOtherSync>();
|
||||||
collection.AddSingleton<IpcManager>();
|
collection.AddSingleton<IpcManager>();
|
||||||
collection.AddSingleton<NotificationService>();
|
collection.AddSingleton<NotificationService>();
|
||||||
collection.AddSingleton<NoSnapService>();
|
|
||||||
|
|
||||||
collection.AddSingleton((s) => new MareConfigService(pluginInterface.ConfigDirectory.FullName));
|
collection.AddSingleton((s) => new MareConfigService(pluginInterface.ConfigDirectory.FullName));
|
||||||
collection.AddSingleton((s) => new ServerConfigService(pluginInterface.ConfigDirectory.FullName));
|
collection.AddSingleton((s) => new ServerConfigService(pluginInterface.ConfigDirectory.FullName));
|
||||||
@@ -245,12 +246,11 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddHostedService(p => p.GetRequiredService<EventAggregator>());
|
collection.AddHostedService(p => p.GetRequiredService<EventAggregator>());
|
||||||
collection.AddHostedService(p => p.GetRequiredService<MarePlugin>());
|
collection.AddHostedService(p => p.GetRequiredService<MarePlugin>());
|
||||||
collection.AddHostedService(p => p.GetRequiredService<IpcProvider>());
|
collection.AddHostedService(p => p.GetRequiredService<IpcProvider>());
|
||||||
collection.AddHostedService(p => p.GetRequiredService<RepoChangeService>());
|
|
||||||
collection.AddHostedService(p => p.GetRequiredService<NoSnapService>());
|
|
||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
_ = Task.Run(async () => {
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _host.StartAsync().ConfigureAwait(false);
|
await _host.StartAsync().ConfigureAwait(false);
|
||||||
@@ -267,4 +267,11 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
_host.StopAsync().GetAwaiter().GetResult();
|
_host.StopAsync().GetAwaiter().GetResult();
|
||||||
_host.Dispose();
|
_host.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ToggleFileLogging(bool enable, string reason)
|
||||||
|
{
|
||||||
|
_traceLogEnabled = true;
|
||||||
|
_host.Services.GetService<ILogger<Plugin>>()?.LogInformation("File tracelog {enabled}: {reason}", enable ? "enabled" : "disabled", reason);
|
||||||
|
_traceLogEnabled = enable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -13,20 +13,18 @@ public sealed class CharaDataCharacterHandler : DisposableMediatorSubscriberBase
|
|||||||
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
|
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
|
||||||
private readonly DalamudUtilService _dalamudUtilService;
|
private readonly DalamudUtilService _dalamudUtilService;
|
||||||
private readonly IpcManager _ipcManager;
|
private readonly IpcManager _ipcManager;
|
||||||
private readonly NoSnapService _noSnapService;
|
|
||||||
private readonly Dictionary<string, HandledCharaDataEntry> _handledCharaData = new(StringComparer.Ordinal);
|
private readonly Dictionary<string, HandledCharaDataEntry> _handledCharaData = new(StringComparer.Ordinal);
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, HandledCharaDataEntry> HandledCharaData => _handledCharaData;
|
public IReadOnlyDictionary<string, HandledCharaDataEntry> HandledCharaData => _handledCharaData;
|
||||||
|
|
||||||
public CharaDataCharacterHandler(ILogger<CharaDataCharacterHandler> logger, MareMediator mediator,
|
public CharaDataCharacterHandler(ILogger<CharaDataCharacterHandler> logger, MareMediator mediator,
|
||||||
GameObjectHandlerFactory gameObjectHandlerFactory, DalamudUtilService dalamudUtilService,
|
GameObjectHandlerFactory gameObjectHandlerFactory, DalamudUtilService dalamudUtilService,
|
||||||
IpcManager ipcManager, NoSnapService noSnapService)
|
IpcManager ipcManager)
|
||||||
: base(logger, mediator)
|
: base(logger, mediator)
|
||||||
{
|
{
|
||||||
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
||||||
_dalamudUtilService = dalamudUtilService;
|
_dalamudUtilService = dalamudUtilService;
|
||||||
_ipcManager = ipcManager;
|
_ipcManager = ipcManager;
|
||||||
_noSnapService = noSnapService;
|
|
||||||
mediator.Subscribe<GposeEndMessage>(this, msg =>
|
mediator.Subscribe<GposeEndMessage>(this, msg =>
|
||||||
{
|
{
|
||||||
foreach (var chara in _handledCharaData)
|
foreach (var chara in _handledCharaData)
|
||||||
@@ -94,7 +92,6 @@ public sealed class CharaDataCharacterHandler : DisposableMediatorSubscriberBase
|
|||||||
_handledCharaData.Remove(handled.Name);
|
_handledCharaData.Remove(handled.Name);
|
||||||
await _dalamudUtilService.RunOnFrameworkThread(async () =>
|
await _dalamudUtilService.RunOnFrameworkThread(async () =>
|
||||||
{
|
{
|
||||||
RemoveGposer(handled);
|
|
||||||
await RevertChara(handled.Name, handled.CustomizePlus).ConfigureAwait(false);
|
await RevertChara(handled.Name, handled.CustomizePlus).ConfigureAwait(false);
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
return true;
|
return true;
|
||||||
@@ -103,7 +100,6 @@ public sealed class CharaDataCharacterHandler : DisposableMediatorSubscriberBase
|
|||||||
internal void AddHandledChara(HandledCharaDataEntry handledCharaDataEntry)
|
internal void AddHandledChara(HandledCharaDataEntry handledCharaDataEntry)
|
||||||
{
|
{
|
||||||
_handledCharaData.Add(handledCharaDataEntry.Name, handledCharaDataEntry);
|
_handledCharaData.Add(handledCharaDataEntry.Name, handledCharaDataEntry);
|
||||||
_ = _dalamudUtilService.RunOnFrameworkThread(() => AddGposer(handledCharaDataEntry));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateHandledData(Dictionary<string, CharaDataMetaInfoExtendedDto?> newData)
|
public void UpdateHandledData(Dictionary<string, CharaDataMetaInfoExtendedDto?> newData)
|
||||||
@@ -134,23 +130,4 @@ public sealed class CharaDataCharacterHandler : DisposableMediatorSubscriberBase
|
|||||||
if (handler.Address == nint.Zero) return null;
|
if (handler.Address == nint.Zero) return null;
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetGposerObjectIndex(string name)
|
|
||||||
{
|
|
||||||
return _dalamudUtilService.GetGposeCharacterFromObjectTableByName(name, _dalamudUtilService.IsInGpose)?.ObjectIndex ?? -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddGposer(HandledCharaDataEntry handled)
|
|
||||||
{
|
|
||||||
int objectIndex = GetGposerObjectIndex(handled.Name);
|
|
||||||
if (objectIndex > 0)
|
|
||||||
_noSnapService.AddGposer(objectIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveGposer(HandledCharaDataEntry handled)
|
|
||||||
{
|
|
||||||
int objectIndex = GetGposerObjectIndex(handled.Name);
|
|
||||||
if (objectIndex > 0)
|
|
||||||
_noSnapService.RemoveGposer(objectIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ public class ChatService : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_chatGui.PrintError($"[LoporritSync] Syncshell number #{shellNumber} not found");
|
_chatGui.PrintError($"[ClubPenguinSync] Syncshell number #{shellNumber} not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendChatShell(int shellNumber, byte[] chatBytes)
|
public void SendChatShell(int shellNumber, byte[] chatBytes)
|
||||||
@@ -236,6 +236,6 @@ public class ChatService : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_chatGui.PrintError($"[LoporritSync] Syncshell number #{shellNumber} not found");
|
_chatGui.PrintError($"[ClubPenguinSync] Syncshell number #{shellNumber} not found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ namespace MareSynchronos.Services;
|
|||||||
public sealed class CommandManagerService : IDisposable
|
public sealed class CommandManagerService : IDisposable
|
||||||
{
|
{
|
||||||
private const string _commandName = "/sync";
|
private const string _commandName = "/sync";
|
||||||
private const string _commandName2 = "/loporrit";
|
private const string _commandName2 = "/clubpenguin";
|
||||||
|
|
||||||
private const string _ssCommandPrefix = "/ss";
|
private const string _ssCommandPrefix = "/ss";
|
||||||
|
|
||||||
@@ -42,11 +42,11 @@ public sealed class CommandManagerService : IDisposable
|
|||||||
_mareConfigService = mareConfigService;
|
_mareConfigService = mareConfigService;
|
||||||
_commandManager.AddHandler(_commandName, new CommandInfo(OnCommand)
|
_commandManager.AddHandler(_commandName, new CommandInfo(OnCommand)
|
||||||
{
|
{
|
||||||
HelpMessage = "Opens the Loporrit UI"
|
HelpMessage = "Opens the Club Penguin Sync UI"
|
||||||
});
|
});
|
||||||
_commandManager.AddHandler(_commandName2, new CommandInfo(OnCommand)
|
_commandManager.AddHandler(_commandName2, new CommandInfo(OnCommand)
|
||||||
{
|
{
|
||||||
HelpMessage = "Opens the Loporrit UI"
|
HelpMessage = "Opens the Club Penguin Sync UI"
|
||||||
});
|
});
|
||||||
|
|
||||||
// Lazy registration of all possible /ss# commands which tbf is what the game does for linkshells anyway
|
// Lazy registration of all possible /ss# commands which tbf is what the game does for linkshells anyway
|
||||||
@@ -86,7 +86,7 @@ public sealed class CommandManagerService : IDisposable
|
|||||||
{
|
{
|
||||||
if (_apiController.ServerState == WebAPI.SignalR.Utils.ServerState.Disconnecting)
|
if (_apiController.ServerState == WebAPI.SignalR.Utils.ServerState.Disconnecting)
|
||||||
{
|
{
|
||||||
_mediator.Publish(new NotificationMessage("Loporrit disconnecting", "Cannot use /toggle while Loporrit is still disconnecting",
|
_mediator.Publish(new NotificationMessage("Club Penguin Sync disconnecting", "Cannot use /toggle while Club Penguin Sync is still disconnecting",
|
||||||
NotificationType.Error));
|
NotificationType.Error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -468,9 +468,9 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
|||||||
{
|
{
|
||||||
_logger.LogInformation("Starting DalamudUtilService");
|
_logger.LogInformation("Starting DalamudUtilService");
|
||||||
#pragma warning disable S2696 // Instance members should not write to "static" fields
|
#pragma warning disable S2696 // Instance members should not write to "static" fields
|
||||||
LoporritSync.Plugin.Self.RealOnFrameworkUpdate = this.FrameworkOnUpdate;
|
ClubPenguinSync.Plugin.Self.RealOnFrameworkUpdate = this.FrameworkOnUpdate;
|
||||||
#pragma warning restore S2696
|
#pragma warning restore S2696
|
||||||
_framework.Update += LoporritSync.Plugin.Self.OnFrameworkUpdate;
|
_framework.Update += ClubPenguinSync.Plugin.Self.OnFrameworkUpdate;
|
||||||
if (IsLoggedIn)
|
if (IsLoggedIn)
|
||||||
{
|
{
|
||||||
_classJobId = _clientState.LocalPlayer!.ClassJob.RowId;
|
_classJobId = _clientState.LocalPlayer!.ClassJob.RowId;
|
||||||
@@ -485,7 +485,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
|||||||
_logger.LogTrace("Stopping {type}", GetType());
|
_logger.LogTrace("Stopping {type}", GetType());
|
||||||
|
|
||||||
Mediator.UnsubscribeAll(this);
|
Mediator.UnsubscribeAll(this);
|
||||||
_framework.Update -= LoporritSync.Plugin.Self.OnFrameworkUpdate;
|
_framework.Update -= ClubPenguinSync.Plugin.Self.OnFrameworkUpdate;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,226 +0,0 @@
|
|||||||
using Dalamud.Plugin;
|
|
||||||
using MareSynchronos.Interop.Ipc;
|
|
||||||
using MareSynchronos.MareConfiguration.Models;
|
|
||||||
using MareSynchronos.Services.Mediator;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace MareSynchronos.Services;
|
|
||||||
|
|
||||||
public sealed class NoSnapService : IHostedService, IMediatorSubscriber
|
|
||||||
{
|
|
||||||
private record NoSnapConfig
|
|
||||||
{
|
|
||||||
[JsonPropertyName("listOfPlugins")]
|
|
||||||
public string[]? ListOfPlugins { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly ILogger<NoSnapService> _logger;
|
|
||||||
private readonly IDalamudPluginInterface _pluginInterface;
|
|
||||||
private readonly Dictionary<string, bool> _listOfPlugins = new(StringComparer.Ordinal)
|
|
||||||
{
|
|
||||||
["Snapper"] = false,
|
|
||||||
["Snappy"] = false,
|
|
||||||
["Meddle.Plugin"] = false,
|
|
||||||
};
|
|
||||||
private static readonly HashSet<int> _gposers = new();
|
|
||||||
private static readonly HashSet<string> _gposersNamed = new(StringComparer.Ordinal);
|
|
||||||
private readonly IHostApplicationLifetime _hostApplicationLifetime;
|
|
||||||
private readonly DalamudUtilService _dalamudUtilService;
|
|
||||||
private readonly IpcManager _ipcManager;
|
|
||||||
private readonly RemoteConfigurationService _remoteConfig;
|
|
||||||
|
|
||||||
public static bool AnyLoaded { get; private set; } = false;
|
|
||||||
public static string ActivePlugins { get; private set; } = string.Empty;
|
|
||||||
|
|
||||||
public MareMediator Mediator { get; init; }
|
|
||||||
|
|
||||||
public NoSnapService(ILogger<NoSnapService> logger, IDalamudPluginInterface pluginInterface, MareMediator mediator,
|
|
||||||
IHostApplicationLifetime hostApplicationLifetime, DalamudUtilService dalamudUtilService, IpcManager ipcManager,
|
|
||||||
RemoteConfigurationService remoteConfig)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_pluginInterface = pluginInterface;
|
|
||||||
Mediator = mediator;
|
|
||||||
_hostApplicationLifetime = hostApplicationLifetime;
|
|
||||||
_dalamudUtilService = dalamudUtilService;
|
|
||||||
_ipcManager = ipcManager;
|
|
||||||
_remoteConfig = remoteConfig;
|
|
||||||
|
|
||||||
Mediator.Subscribe<GposeEndMessage>(this, msg => ClearGposeList());
|
|
||||||
Mediator.Subscribe<CutsceneEndMessage>(this, msg => ClearGposeList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddGposer(int objectIndex)
|
|
||||||
{
|
|
||||||
if (AnyLoaded || _hostApplicationLifetime.ApplicationStopping.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
_logger.LogTrace("Immediately reverting object index {id}", objectIndex);
|
|
||||||
RevertAndRedraw(objectIndex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogTrace("Registering gposer object index {id}", objectIndex);
|
|
||||||
lock (_gposers)
|
|
||||||
_gposers.Add(objectIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveGposer(int objectIndex)
|
|
||||||
{
|
|
||||||
_logger.LogTrace("Un-registering gposer object index {id}", objectIndex);
|
|
||||||
lock (_gposers)
|
|
||||||
_gposers.Remove(objectIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddGposerNamed(string name)
|
|
||||||
{
|
|
||||||
if (AnyLoaded || _hostApplicationLifetime.ApplicationStopping.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
_logger.LogTrace("Immediately reverting {name}", name);
|
|
||||||
RevertAndRedraw(name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogTrace("Registering gposer {name}", name);
|
|
||||||
lock (_gposers)
|
|
||||||
_gposersNamed.Add(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearGposeList()
|
|
||||||
{
|
|
||||||
if (_gposers.Count > 0 || _gposersNamed.Count > 0)
|
|
||||||
_logger.LogTrace("Clearing gposer list");
|
|
||||||
lock (_gposers)
|
|
||||||
_gposers.Clear();
|
|
||||||
lock (_gposersNamed)
|
|
||||||
_gposersNamed.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RevertAndRedraw(int objIndex, Guid applicationId = default)
|
|
||||||
{
|
|
||||||
if (applicationId == default)
|
|
||||||
applicationId = Guid.NewGuid();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_ipcManager.Glamourer.RevertNow(_logger, applicationId, objIndex);
|
|
||||||
_ipcManager.Penumbra.RedrawNow(_logger, applicationId, objIndex);
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RevertAndRedraw(string name, Guid applicationId = default)
|
|
||||||
{
|
|
||||||
if (applicationId == default)
|
|
||||||
applicationId = Guid.NewGuid();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_ipcManager.Glamourer.RevertByNameNow(_logger, applicationId, name);
|
|
||||||
var addr = _dalamudUtilService.GetPlayerCharacterFromCachedTableByName(name);
|
|
||||||
if (addr != 0)
|
|
||||||
{
|
|
||||||
var obj = _dalamudUtilService.CreateGameObject(addr);
|
|
||||||
if (obj != null)
|
|
||||||
_ipcManager.Penumbra.RedrawNow(_logger, applicationId, obj.ObjectIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RevertGposers()
|
|
||||||
{
|
|
||||||
List<int>? gposersList = null;
|
|
||||||
List<string>? gposersList2 = null;
|
|
||||||
|
|
||||||
lock (_gposers)
|
|
||||||
{
|
|
||||||
if (_gposers.Count > 0)
|
|
||||||
{
|
|
||||||
gposersList = _gposers.ToList();
|
|
||||||
_gposers.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_gposersNamed)
|
|
||||||
{
|
|
||||||
if (_gposersNamed.Count > 0)
|
|
||||||
{
|
|
||||||
gposersList2 = _gposersNamed.ToList();
|
|
||||||
_gposersNamed.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gposersList == null && gposersList2 == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_logger.LogInformation("Reverting gposers");
|
|
||||||
|
|
||||||
_dalamudUtilService.RunOnFrameworkThread(() =>
|
|
||||||
{
|
|
||||||
Guid applicationId = Guid.NewGuid();
|
|
||||||
|
|
||||||
foreach (var gposer in gposersList ?? [])
|
|
||||||
RevertAndRedraw(gposer, applicationId);
|
|
||||||
|
|
||||||
foreach (var gposerName in gposersList2 ?? [])
|
|
||||||
RevertAndRedraw(gposerName, applicationId);
|
|
||||||
}).GetAwaiter().GetResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task StartAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var config = await _remoteConfig.GetConfigAsync<NoSnapConfig>("noSnap").ConfigureAwait(false) ?? new();
|
|
||||||
|
|
||||||
if (config.ListOfPlugins != null)
|
|
||||||
{
|
|
||||||
_listOfPlugins.Clear();
|
|
||||||
foreach (var pluginName in config.ListOfPlugins)
|
|
||||||
_listOfPlugins.TryAdd(pluginName, value: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var pluginName in _listOfPlugins.Keys)
|
|
||||||
{
|
|
||||||
_listOfPlugins[pluginName] = PluginWatcherService.GetInitialPluginState(_pluginInterface, pluginName)?.IsLoaded ?? false;
|
|
||||||
Mediator.SubscribeKeyed<PluginChangeMessage>(this, pluginName, (msg) =>
|
|
||||||
{
|
|
||||||
_listOfPlugins[pluginName] = msg.IsLoaded;
|
|
||||||
_logger.LogDebug("{pluginName} isLoaded = {isLoaded}", pluginName, msg.IsLoaded);
|
|
||||||
Update();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
RevertGposers();
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
bool anyLoadedNow = _listOfPlugins.Values.Any(p => p);
|
|
||||||
|
|
||||||
if (AnyLoaded != anyLoadedNow)
|
|
||||||
{
|
|
||||||
AnyLoaded = anyLoadedNow;
|
|
||||||
Mediator.Publish(new RecalculatePerformanceMessage(null));
|
|
||||||
|
|
||||||
if (AnyLoaded)
|
|
||||||
{
|
|
||||||
RevertGposers();
|
|
||||||
var pluginList = string.Join(", ", _listOfPlugins.Where(p => p.Value).Select(p => p.Key));
|
|
||||||
Mediator.Publish(new NotificationMessage("Incompatible plugin loaded", $"Synced player appearances will not apply until incompatible plugins are disabled: {pluginList}.",
|
|
||||||
NotificationType.Error));
|
|
||||||
ActivePlugins = pluginList;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ActivePlugins = string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -41,19 +41,19 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
|||||||
|
|
||||||
private void PrintErrorChat(string? message)
|
private void PrintErrorChat(string? message)
|
||||||
{
|
{
|
||||||
SeStringBuilder se = new SeStringBuilder().AddText("[LoporritSync] Error: " + message);
|
SeStringBuilder se = new SeStringBuilder().AddText("[ClubPenguinSync] Error: " + message);
|
||||||
_chatGui.PrintError(se.BuiltString);
|
_chatGui.PrintError(se.BuiltString);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PrintInfoChat(string? message)
|
private void PrintInfoChat(string? message)
|
||||||
{
|
{
|
||||||
SeStringBuilder se = new SeStringBuilder().AddText("[LoporritSync] Info: ").AddItalics(message ?? string.Empty);
|
SeStringBuilder se = new SeStringBuilder().AddText("[ClubPenguinSync] Info: ").AddItalics(message ?? string.Empty);
|
||||||
_chatGui.Print(se.BuiltString);
|
_chatGui.Print(se.BuiltString);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PrintWarnChat(string? message)
|
private void PrintWarnChat(string? message)
|
||||||
{
|
{
|
||||||
SeStringBuilder se = new SeStringBuilder().AddText("[LoporritSync] ").AddUiForeground("Warning: " + (message ?? string.Empty), 31).AddUiForegroundOff();
|
SeStringBuilder se = new SeStringBuilder().AddText("[ClubPenguinSync] ").AddUiForeground("Warning: " + (message ?? string.Empty), 31).AddUiForegroundOff();
|
||||||
_chatGui.Print(se.BuiltString);
|
_chatGui.Print(se.BuiltString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ public sealed class RemoteConfigurationService
|
|||||||
{
|
{
|
||||||
private readonly static Dictionary<string, string> ConfigPublicKeys = new(StringComparer.Ordinal)
|
private readonly static Dictionary<string, string> ConfigPublicKeys = new(StringComparer.Ordinal)
|
||||||
{
|
{
|
||||||
{ "4D6633E0", "GWRoAiXP9lcn9/34wGgziYcqQH8f6zWtZrRyp66Ekso=" },
|
{ "3PC5J4C4", "rhUOaY2Y7doUY0GDmOEgM5CoyzxLqCjOpsdO6O+rTjE=" },
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly static string[] ConfigSources = [
|
private readonly static string[] ConfigSources = [
|
||||||
"https://plugin.lop-sync.com/config/config.json",
|
"https://clubpenguin.drgn.rocks/config.json",
|
||||||
"https://plugin.lop-sync.net/config/config.json",
|
"https://clubpenguin.drgn.rocks/config.json",
|
||||||
];
|
];
|
||||||
|
|
||||||
private readonly ILogger<RemoteConfigurationService> _logger;
|
private readonly ILogger<RemoteConfigurationService> _logger;
|
||||||
@@ -185,6 +185,7 @@ public sealed class RemoteConfigurationService
|
|||||||
|
|
||||||
var signatures = jsonDoc["sig"]!.AsObject();
|
var signatures = jsonDoc["sig"]!.AsObject();
|
||||||
var configString = jsonDoc["config"]!.GetValue<string>();
|
var configString = jsonDoc["config"]!.GetValue<string>();
|
||||||
|
|
||||||
bool verified = signatures.Any(sig =>
|
bool verified = signatures.Any(sig =>
|
||||||
ConfigPublicKeys.TryGetValue(sig.Key, out var pubKey) &&
|
ConfigPublicKeys.TryGetValue(sig.Key, out var pubKey) &&
|
||||||
VerifySignature(configString, ts, sig.Value!.GetValue<string>(), pubKey));
|
VerifySignature(configString, ts, sig.Value!.GetValue<string>(), pubKey));
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace MareSynchronos.Services;
|
|
||||||
|
|
||||||
public record RepoChangeConfig
|
|
||||||
{
|
|
||||||
[JsonPropertyName("current_repo")]
|
|
||||||
public string? CurrentRepo { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("valid_repos")]
|
|
||||||
public string[]? ValidRepos { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,401 +0,0 @@
|
|||||||
using Dalamud.Plugin;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using Dalamud.Utility;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace MareSynchronos.Services;
|
|
||||||
|
|
||||||
/* Reflection code based almost entirely on ECommons DalamudReflector
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2023 NightmareXIV
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
public sealed class RepoChangeService : IHostedService
|
|
||||||
{
|
|
||||||
#region Reflection Helpers
|
|
||||||
private const BindingFlags AllFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
|
|
||||||
private const BindingFlags StaticFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
|
|
||||||
private const BindingFlags InstanceFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
|
|
||||||
|
|
||||||
private static object GetFoP(object obj, string name)
|
|
||||||
{
|
|
||||||
Type? type = obj.GetType();
|
|
||||||
while (type != null)
|
|
||||||
{
|
|
||||||
var fieldInfo = type.GetField(name, AllFlags);
|
|
||||||
if (fieldInfo != null)
|
|
||||||
{
|
|
||||||
return fieldInfo.GetValue(obj)!;
|
|
||||||
}
|
|
||||||
var propertyInfo = type.GetProperty(name, AllFlags);
|
|
||||||
if (propertyInfo != null)
|
|
||||||
{
|
|
||||||
return propertyInfo.GetValue(obj)!;
|
|
||||||
}
|
|
||||||
type = type.BaseType;
|
|
||||||
}
|
|
||||||
throw new Exception($"Reflection GetFoP failed (not found: {obj.GetType().Name}.{name})");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static T GetFoP<T>(object obj, string name)
|
|
||||||
{
|
|
||||||
return (T)GetFoP(obj, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetFoP(object obj, string name, object value)
|
|
||||||
{
|
|
||||||
var type = obj.GetType();
|
|
||||||
var field = type.GetField(name, AllFlags);
|
|
||||||
if (field != null)
|
|
||||||
{
|
|
||||||
field.SetValue(obj, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var prop = type.GetProperty(name, AllFlags)!;
|
|
||||||
if (prop == null)
|
|
||||||
throw new Exception($"Reflection SetFoP failed (not found: {type.Name}.{name})");
|
|
||||||
prop.SetValue(obj, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object? Call(object obj, string name, object[] @params, bool matchExactArgumentTypes = false)
|
|
||||||
{
|
|
||||||
MethodInfo? info;
|
|
||||||
var type = obj.GetType();
|
|
||||||
if (!matchExactArgumentTypes)
|
|
||||||
{
|
|
||||||
info = type.GetMethod(name, AllFlags);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
info = type.GetMethod(name, AllFlags, @params.Select(x => x.GetType()).ToArray());
|
|
||||||
}
|
|
||||||
if (info == null)
|
|
||||||
throw new Exception($"Reflection Call failed (not found: {type.Name}.{name})");
|
|
||||||
return info.Invoke(obj, @params);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static T Call<T>(object obj, string name, object[] @params, bool matchExactArgumentTypes = false)
|
|
||||||
{
|
|
||||||
return (T)Call(obj, name, @params, matchExactArgumentTypes)!;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Dalamud Reflection
|
|
||||||
public object GetService(string serviceFullName)
|
|
||||||
{
|
|
||||||
return _pluginInterface.GetType().Assembly.
|
|
||||||
GetType("Dalamud.Service`1", true)!.MakeGenericType(_pluginInterface.GetType().Assembly.GetType(serviceFullName, true)!).
|
|
||||||
GetMethod("Get")!.Invoke(null, BindingFlags.Default, null, Array.Empty<object>(), null)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetPluginManager()
|
|
||||||
{
|
|
||||||
return _pluginInterface.GetType().Assembly.
|
|
||||||
GetType("Dalamud.Service`1", true)!.MakeGenericType(_pluginInterface.GetType().Assembly.GetType("Dalamud.Plugin.Internal.PluginManager", true)!).
|
|
||||||
GetMethod("Get")!.Invoke(null, BindingFlags.Default, null, Array.Empty<object>(), null)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReloadPluginMasters()
|
|
||||||
{
|
|
||||||
var mgr = GetService("Dalamud.Plugin.Internal.PluginManager");
|
|
||||||
var pluginReload = mgr.GetType().GetMethod("SetPluginReposFromConfigAsync", BindingFlags.Instance | BindingFlags.Public)!;
|
|
||||||
pluginReload.Invoke(mgr, [true]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SaveDalamudConfig()
|
|
||||||
{
|
|
||||||
var conf = GetService("Dalamud.Configuration.Internal.DalamudConfiguration");
|
|
||||||
var configSave = conf?.GetType().GetMethod("QueueSave", BindingFlags.Instance | BindingFlags.Public);
|
|
||||||
configSave?.Invoke(conf, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<object> GetRepoByURL(string repoURL)
|
|
||||||
{
|
|
||||||
var conf = GetService("Dalamud.Configuration.Internal.DalamudConfiguration");
|
|
||||||
var repolist = (System.Collections.IEnumerable)GetFoP(conf, "ThirdRepoList");
|
|
||||||
foreach (var r in repolist)
|
|
||||||
{
|
|
||||||
if (((string)GetFoP(r, "Url")).Equals(repoURL, StringComparison.OrdinalIgnoreCase))
|
|
||||||
yield return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool HasRepo(string repoURL)
|
|
||||||
{
|
|
||||||
var conf = GetService("Dalamud.Configuration.Internal.DalamudConfiguration");
|
|
||||||
var repolist = (System.Collections.IEnumerable)GetFoP(conf, "ThirdRepoList");
|
|
||||||
foreach (var r in repolist)
|
|
||||||
{
|
|
||||||
if (((string)GetFoP(r, "Url")).Equals(repoURL, StringComparison.OrdinalIgnoreCase))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddRepo(string repoURL, bool enabled)
|
|
||||||
{
|
|
||||||
var conf = GetService("Dalamud.Configuration.Internal.DalamudConfiguration");
|
|
||||||
var repolist = (System.Collections.IEnumerable)GetFoP(conf, "ThirdRepoList");
|
|
||||||
foreach (var r in repolist)
|
|
||||||
{
|
|
||||||
if (((string)GetFoP(r, "Url")).Equals(repoURL, StringComparison.OrdinalIgnoreCase))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var instance = Activator.CreateInstance(_pluginInterface.GetType().Assembly.GetType("Dalamud.Configuration.ThirdPartyRepoSettings")!)!;
|
|
||||||
SetFoP(instance, "Url", repoURL);
|
|
||||||
SetFoP(instance, "IsEnabled", enabled);
|
|
||||||
GetFoP<System.Collections.IList>(conf, "ThirdRepoList").Add(instance!);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveRepo(string repoURL)
|
|
||||||
{
|
|
||||||
var toRemove = new List<object>();
|
|
||||||
var conf = GetService("Dalamud.Configuration.Internal.DalamudConfiguration");
|
|
||||||
var repolist = (System.Collections.IList)GetFoP(conf, "ThirdRepoList");
|
|
||||||
foreach (var r in repolist)
|
|
||||||
{
|
|
||||||
if (((string)GetFoP(r, "Url")).Equals(repoURL, StringComparison.OrdinalIgnoreCase))
|
|
||||||
toRemove.Add(r);
|
|
||||||
}
|
|
||||||
foreach (var r in toRemove)
|
|
||||||
repolist.Remove(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<(object LocalPlugin, string InstalledFromUrl)> GetLocalPluginsByName(string internalName)
|
|
||||||
{
|
|
||||||
List<(object LocalPlugin, string RepoURL)> result = [];
|
|
||||||
|
|
||||||
var pluginManager = GetPluginManager();
|
|
||||||
var installedPlugins = (System.Collections.IList)pluginManager.GetType().GetProperty("InstalledPlugins")!.GetValue(pluginManager)!;
|
|
||||||
|
|
||||||
foreach (var plugin in installedPlugins)
|
|
||||||
{
|
|
||||||
if (((string)plugin.GetType().GetProperty("InternalName")!.GetValue(plugin)!).Equals(internalName, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
var type = plugin.GetType();
|
|
||||||
if (type.Name.Equals("LocalDevPlugin", StringComparison.Ordinal))
|
|
||||||
continue;
|
|
||||||
var manifest = GetFoP(plugin, "manifest");
|
|
||||||
string installedFromUrl = (string)GetFoP(manifest, "InstalledFromUrl");
|
|
||||||
result.Add((plugin, installedFromUrl));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private readonly ILogger<RepoChangeService> _logger;
|
|
||||||
private readonly RemoteConfigurationService _remoteConfig;
|
|
||||||
private readonly IDalamudPluginInterface _pluginInterface;
|
|
||||||
private readonly IFramework _framework;
|
|
||||||
|
|
||||||
public RepoChangeService(ILogger<RepoChangeService> logger, RemoteConfigurationService remoteConfig, IDalamudPluginInterface pluginInterface, IFramework framework)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_remoteConfig = remoteConfig;
|
|
||||||
_pluginInterface = pluginInterface;
|
|
||||||
_framework = framework;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task StartAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Starting RepoChange Service");
|
|
||||||
var repoChangeConfig = await _remoteConfig.GetConfigAsync<RepoChangeConfig>("repoChange").ConfigureAwait(false) ?? new();
|
|
||||||
|
|
||||||
var currentRepo = repoChangeConfig.CurrentRepo;
|
|
||||||
var validRepos = (repoChangeConfig.ValidRepos ?? []).ToList();
|
|
||||||
|
|
||||||
if (!currentRepo.IsNullOrEmpty() && !validRepos.Contains(currentRepo, StringComparer.Ordinal))
|
|
||||||
validRepos.Add(currentRepo);
|
|
||||||
|
|
||||||
if (validRepos.Count == 0)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("No valid repos configured, skipping");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await _framework.RunOnTick(() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var internalName = Assembly.GetExecutingAssembly().GetName().Name!;
|
|
||||||
var localPlugins = GetLocalPluginsByName(internalName);
|
|
||||||
|
|
||||||
var suffix = string.Empty;
|
|
||||||
|
|
||||||
if (localPlugins.Count == 0)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Skipping: No intalled plugin found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasValidCustomRepoUrl = false;
|
|
||||||
|
|
||||||
foreach (var vr in validRepos)
|
|
||||||
{
|
|
||||||
var vrCN = vr.Replace(".json", "_CN.json", StringComparison.Ordinal);
|
|
||||||
var vrKR = vr.Replace(".json", "_KR.json", StringComparison.Ordinal);
|
|
||||||
if (HasRepo(vr) || HasRepo(vrCN) || HasRepo(vrKR))
|
|
||||||
{
|
|
||||||
hasValidCustomRepoUrl = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<string> oldRepos = [];
|
|
||||||
var pluginRepoUrl = localPlugins[0].InstalledFromUrl;
|
|
||||||
|
|
||||||
if (pluginRepoUrl.Contains("_CN.json", StringComparison.Ordinal))
|
|
||||||
suffix = "_CN";
|
|
||||||
else if (pluginRepoUrl.Contains("_KR.json", StringComparison.Ordinal))
|
|
||||||
suffix = "_KR";
|
|
||||||
|
|
||||||
bool hasOldPluginRepoUrl = false;
|
|
||||||
|
|
||||||
foreach (var plugin in localPlugins)
|
|
||||||
{
|
|
||||||
foreach (var vr in validRepos)
|
|
||||||
{
|
|
||||||
var validRepo = vr.Replace(".json", $"{suffix}.json");
|
|
||||||
if (!plugin.InstalledFromUrl.Equals(validRepo, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
oldRepos.Add(plugin.InstalledFromUrl);
|
|
||||||
hasOldPluginRepoUrl = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasValidCustomRepoUrl)
|
|
||||||
{
|
|
||||||
if (hasOldPluginRepoUrl)
|
|
||||||
_logger.LogInformation("Result: Repo URL is up to date, but plugin install source is incorrect");
|
|
||||||
else
|
|
||||||
_logger.LogInformation("Result: Repo URL is up to date");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Result: Repo URL needs to be replaced");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentRepo.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
_logger.LogWarning("No current repo URL configured");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-test plugin repo url rewriting to ensure it succeeds before replacing the custom repo URL
|
|
||||||
if (hasOldPluginRepoUrl)
|
|
||||||
{
|
|
||||||
foreach (var plugin in localPlugins)
|
|
||||||
{
|
|
||||||
var manifest = GetFoP(plugin.LocalPlugin, "manifest");
|
|
||||||
if (manifest == null)
|
|
||||||
throw new Exception("Plugin manifest is null");
|
|
||||||
var manifestFile = GetFoP(plugin.LocalPlugin, "manifestFile");
|
|
||||||
if (manifestFile == null)
|
|
||||||
throw new Exception("Plugin manifestFile is null");
|
|
||||||
var repo = GetFoP(manifest, "InstalledFromUrl");
|
|
||||||
if (((string)repo).IsNullOrEmpty())
|
|
||||||
throw new Exception("Plugin repo url is null or empty");
|
|
||||||
SetFoP(manifest, "InstalledFromUrl", repo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasValidCustomRepoUrl)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
foreach (var oldRepo in oldRepos)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("* Removing old repo: {r}", oldRepo);
|
|
||||||
RemoveRepo(oldRepo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_logger.LogInformation("* Adding current repo: {r}", currentRepo);
|
|
||||||
AddRepo(currentRepo, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This time do it for real, and crash the game if we fail, to avoid saving a broken state
|
|
||||||
if (hasOldPluginRepoUrl)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_logger.LogInformation("* Updating plugins");
|
|
||||||
foreach (var plugin in localPlugins)
|
|
||||||
{
|
|
||||||
var manifest = GetFoP(plugin.LocalPlugin, "manifest");
|
|
||||||
if (manifest == null)
|
|
||||||
throw new Exception("Plugin manifest is null");
|
|
||||||
var manifestFile = GetFoP(plugin.LocalPlugin, "manifestFile");
|
|
||||||
if (manifestFile == null)
|
|
||||||
throw new Exception("Plugin manifestFile is null");
|
|
||||||
var repo = GetFoP(manifest, "InstalledFromUrl");
|
|
||||||
if (((string)repo).IsNullOrEmpty())
|
|
||||||
throw new Exception("Plugin repo url is null or empty");
|
|
||||||
SetFoP(manifest, "InstalledFromUrl", currentRepo);
|
|
||||||
Call(manifest, "Save", [manifestFile, "RepoChange"]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Exception while changing plugin install repo");
|
|
||||||
foreach (var oldRepo in oldRepos)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("* Restoring old repo: {r}", oldRepo);
|
|
||||||
AddRepo(oldRepo, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasValidCustomRepoUrl || hasOldPluginRepoUrl)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("* Saving dalamud config");
|
|
||||||
SaveDalamudConfig();
|
|
||||||
_logger.LogInformation("* Reloading plugin masters");
|
|
||||||
ReloadPluginMasters();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Exception in RepoChangeService");
|
|
||||||
}
|
|
||||||
}, default, 10, cancellationToken).ConfigureAwait(false);
|
|
||||||
_logger.LogInformation("Started RepoChangeService");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
_ = cancellationToken;
|
|
||||||
_logger.LogDebug("Stopping RepoChange Service");
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -496,17 +496,17 @@ public class ServerConfigurationManager
|
|||||||
|
|
||||||
private void EnsureMainExists()
|
private void EnsureMainExists()
|
||||||
{
|
{
|
||||||
bool lopExists = false;
|
bool clubExists = false;
|
||||||
for (int i = 0; i < _configService.Current.ServerStorage.Count; ++i)
|
for (int i = 0; i < _configService.Current.ServerStorage.Count; ++i)
|
||||||
{
|
{
|
||||||
var x = _configService.Current.ServerStorage[i];
|
var x = _configService.Current.ServerStorage[i];
|
||||||
if (x.ServerUri.Equals(ApiController.LoporritServiceUri, StringComparison.OrdinalIgnoreCase))
|
if (x.ServerUri.Equals(ApiController.ClubPenguinServiceUri, StringComparison.OrdinalIgnoreCase))
|
||||||
lopExists = true;
|
clubExists = true;
|
||||||
}
|
}
|
||||||
if (!lopExists)
|
if (!clubExists)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Re-adding missing server {uri}", ApiController.LoporritServiceUri);
|
_logger.LogDebug("Re-adding missing server {uri}", ApiController.ClubPenguinServiceUri);
|
||||||
_configService.Current.ServerStorage.Insert(0, new ServerStorage() { ServerUri = ApiController.LoporritServiceUri, ServerName = ApiController.LoporritServer });
|
_configService.Current.ServerStorage.Insert(0, new ServerStorage() { ServerUri = ApiController.ClubPenguinServiceUri, ServerName = ApiController.ClubPenguinServer });
|
||||||
if (_configService.Current.CurrentServer >= 0)
|
if (_configService.Current.CurrentServer >= 0)
|
||||||
_configService.Current.CurrentServer++;
|
_configService.Current.CurrentServer++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,21 +12,21 @@ public class VisibilityService : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
NotVisible,
|
NotVisible,
|
||||||
Visible,
|
Visible,
|
||||||
MareHandled
|
OtherSyncHandled
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly DalamudUtilService _dalamudUtil;
|
private readonly DalamudUtilService _dalamudUtil;
|
||||||
private readonly ConcurrentDictionary<string, TrackedPlayerStatus> _trackedPlayerVisibility = new(StringComparer.Ordinal);
|
private readonly ConcurrentDictionary<string, TrackedPlayerStatus> _trackedPlayerVisibility = new(StringComparer.Ordinal);
|
||||||
private readonly List<string> _makeVisibleNextFrame = new();
|
private readonly List<string> _makeVisibleNextFrame = new();
|
||||||
private readonly IpcCallerMare _mare;
|
private readonly IpcCallerOtherSync _otherSync;
|
||||||
private readonly HashSet<nint> cachedMareAddresses = new();
|
private readonly HashSet<nint> cachedOtherSyncAddresses = new();
|
||||||
private uint _cachedAddressSum = 0;
|
private uint _cachedAddressSum = 0;
|
||||||
private uint _cachedAddressSumDebounce = 1;
|
private uint _cachedAddressSumDebounce = 1;
|
||||||
|
|
||||||
public VisibilityService(ILogger<VisibilityService> logger, MareMediator mediator, IpcCallerMare mare, DalamudUtilService dalamudUtil)
|
public VisibilityService(ILogger<VisibilityService> logger, MareMediator mediator, IpcCallerOtherSync otherSync, DalamudUtilService dalamudUtil)
|
||||||
: base(logger, mediator)
|
: base(logger, mediator)
|
||||||
{
|
{
|
||||||
_mare = mare;
|
_otherSync = otherSync;
|
||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
|
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
|
||||||
}
|
}
|
||||||
@@ -44,19 +44,19 @@ public class VisibilityService : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
private void FrameworkUpdate()
|
private void FrameworkUpdate()
|
||||||
{
|
{
|
||||||
var mareHandledAddresses = _mare.GetHandledGameAddresses();
|
var otherSyncHandledAddresses = _otherSync.GetHandledGameAddresses();
|
||||||
uint addressSum = 0;
|
uint addressSum = 0;
|
||||||
|
|
||||||
foreach (var addr in mareHandledAddresses)
|
foreach (var addr in otherSyncHandledAddresses)
|
||||||
addressSum ^= (uint)addr.GetHashCode();
|
addressSum ^= (uint)addr.GetHashCode();
|
||||||
|
|
||||||
if (addressSum != _cachedAddressSum)
|
if (addressSum != _cachedAddressSum)
|
||||||
{
|
{
|
||||||
if (addressSum == _cachedAddressSumDebounce)
|
if (addressSum == _cachedAddressSumDebounce)
|
||||||
{
|
{
|
||||||
cachedMareAddresses.Clear();
|
cachedOtherSyncAddresses.Clear();
|
||||||
foreach (var addr in mareHandledAddresses)
|
foreach (var addr in otherSyncHandledAddresses)
|
||||||
cachedMareAddresses.Add(addr);
|
cachedOtherSyncAddresses.Add(addr);
|
||||||
_cachedAddressSum = addressSum;
|
_cachedAddressSum = addressSum;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -69,15 +69,15 @@ public class VisibilityService : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
string ident = player.Key;
|
string ident = player.Key;
|
||||||
var findResult = _dalamudUtil.FindPlayerByNameHash(ident);
|
var findResult = _dalamudUtil.FindPlayerByNameHash(ident);
|
||||||
var isMareHandled = cachedMareAddresses.Contains(findResult.Address);
|
var isOtherSyncHandled = cachedOtherSyncAddresses.Contains(findResult.Address);
|
||||||
var isVisible = findResult.ObjectId != 0 && !isMareHandled;
|
var isVisible = findResult.ObjectId != 0 && !isOtherSyncHandled;
|
||||||
|
|
||||||
if (player.Value == TrackedPlayerStatus.MareHandled && !isMareHandled)
|
if (player.Value == TrackedPlayerStatus.OtherSyncHandled && !isOtherSyncHandled)
|
||||||
_trackedPlayerVisibility.TryUpdate(ident, newValue: TrackedPlayerStatus.NotVisible, comparisonValue: TrackedPlayerStatus.MareHandled);
|
_trackedPlayerVisibility.TryUpdate(ident, newValue: TrackedPlayerStatus.NotVisible, comparisonValue: TrackedPlayerStatus.OtherSyncHandled);
|
||||||
|
|
||||||
if (player.Value == TrackedPlayerStatus.NotVisible && isVisible)
|
if (player.Value == TrackedPlayerStatus.NotVisible && isVisible)
|
||||||
{
|
{
|
||||||
if (_makeVisibleNextFrame.Contains(ident))
|
if (_makeVisibleNextFrame.Contains(ident, StringComparer.Ordinal))
|
||||||
{
|
{
|
||||||
if (_trackedPlayerVisibility.TryUpdate(ident, newValue: TrackedPlayerStatus.Visible, comparisonValue: TrackedPlayerStatus.NotVisible))
|
if (_trackedPlayerVisibility.TryUpdate(ident, newValue: TrackedPlayerStatus.Visible, comparisonValue: TrackedPlayerStatus.NotVisible))
|
||||||
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: true));
|
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: true));
|
||||||
@@ -85,17 +85,17 @@ public class VisibilityService : DisposableMediatorSubscriberBase
|
|||||||
else
|
else
|
||||||
_makeVisibleNextFrame.Add(ident);
|
_makeVisibleNextFrame.Add(ident);
|
||||||
}
|
}
|
||||||
else if (player.Value == TrackedPlayerStatus.NotVisible && isMareHandled)
|
else if (player.Value == TrackedPlayerStatus.NotVisible && isOtherSyncHandled)
|
||||||
{
|
{
|
||||||
// Send a technically redundant visibility update with the added intent of triggering PairHandler to undo the application by name
|
// Send a technically redundant visibility update with the added intent of triggering PairHandler to undo the application by name
|
||||||
if (_trackedPlayerVisibility.TryUpdate(ident, newValue: TrackedPlayerStatus.MareHandled, comparisonValue: TrackedPlayerStatus.NotVisible))
|
if (_trackedPlayerVisibility.TryUpdate(ident, newValue: TrackedPlayerStatus.OtherSyncHandled, comparisonValue: TrackedPlayerStatus.NotVisible))
|
||||||
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: false, Invalidate: true));
|
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: false, Invalidate: true));
|
||||||
}
|
}
|
||||||
else if (player.Value == TrackedPlayerStatus.Visible && !isVisible)
|
else if (player.Value == TrackedPlayerStatus.Visible && !isVisible)
|
||||||
{
|
{
|
||||||
var newTrackedStatus = isMareHandled ? TrackedPlayerStatus.MareHandled : TrackedPlayerStatus.NotVisible;
|
var newTrackedStatus = isOtherSyncHandled ? TrackedPlayerStatus.OtherSyncHandled : TrackedPlayerStatus.NotVisible;
|
||||||
if (_trackedPlayerVisibility.TryUpdate(ident, newValue: newTrackedStatus, comparisonValue: TrackedPlayerStatus.Visible))
|
if (_trackedPlayerVisibility.TryUpdate(ident, newValue: newTrackedStatus, comparisonValue: TrackedPlayerStatus.Visible))
|
||||||
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: false, Invalidate: isMareHandled));
|
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: false, Invalidate: isOtherSyncHandled));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isVisible)
|
if (!isVisible)
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ internal sealed partial class CharaDataHubUi : WindowMediatorSubscriberBase
|
|||||||
UiSharedService uiSharedService, ServerConfigurationManager serverConfigurationManager,
|
UiSharedService uiSharedService, ServerConfigurationManager serverConfigurationManager,
|
||||||
DalamudUtilService dalamudUtilService, FileDialogManager fileDialogManager, PairManager pairManager,
|
DalamudUtilService dalamudUtilService, FileDialogManager fileDialogManager, PairManager pairManager,
|
||||||
CharaDataGposeTogetherManager charaDataGposeTogetherManager)
|
CharaDataGposeTogetherManager charaDataGposeTogetherManager)
|
||||||
: base(logger, mediator, "Loporrit Character Data Hub###LoporritCharaDataUI", performanceCollectorService)
|
: base(logger, mediator, "Club Penguin Character Data Hub###ClubPenguinSyncCharaDataUI", performanceCollectorService)
|
||||||
{
|
{
|
||||||
SetWindowSizeConstraints();
|
SetWindowSizeConstraints();
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
public CompactUi(ILogger<CompactUi> logger, UiSharedService uiShared, MareConfigService configService, ApiController apiController, PairManager pairManager, ChatService chatService,
|
public CompactUi(ILogger<CompactUi> logger, UiSharedService uiShared, MareConfigService configService, ApiController apiController, PairManager pairManager, ChatService chatService,
|
||||||
ServerConfigurationManager serverManager, MareMediator mediator, FileUploadManager fileTransferManager, UidDisplayHandler uidDisplayHandler, CharaDataManager charaDataManager,
|
ServerConfigurationManager serverManager, MareMediator mediator, FileUploadManager fileTransferManager, UidDisplayHandler uidDisplayHandler, CharaDataManager charaDataManager,
|
||||||
PerformanceCollectorService performanceCollectorService)
|
PerformanceCollectorService performanceCollectorService)
|
||||||
: base(logger, mediator, "###LoporritSyncMainUI", performanceCollectorService)
|
: base(logger, mediator, "###ClubPenguinSyncMainUI", performanceCollectorService)
|
||||||
{
|
{
|
||||||
_uiSharedService = uiShared;
|
_uiSharedService = uiShared;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
@@ -80,11 +80,11 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
string dev = "Dev Build";
|
string dev = "Dev Build";
|
||||||
var ver = Assembly.GetExecutingAssembly().GetName().Version!;
|
var ver = Assembly.GetExecutingAssembly().GetName().Version!;
|
||||||
WindowName = $"Loporrit Sync {dev} ({ver.Major}.{ver.Minor}.{ver.Build})###LoporritSyncMainUIDev";
|
WindowName = $"Club PenguinS ync {dev} ({ver.Major}.{ver.Minor}.{ver.Build}.{ver.Revision})###ClubPenguinSyncMainUIDev";
|
||||||
Toggle();
|
Toggle();
|
||||||
#else
|
#else
|
||||||
var ver = Assembly.GetExecutingAssembly().GetName().Version!;
|
var ver = Assembly.GetExecutingAssembly().GetName().Version!;
|
||||||
WindowName = "Loporrit Sync " + ver.Major + "." + ver.Minor + "." + ver.Build + "###LoporritSyncMainUI";
|
WindowName = $"Club Penguin Sync {ver.Major}.{ver.Minor}.{ver.Build}.{ver.Revision}###ClubPenguinSyncMainUI";
|
||||||
#endif
|
#endif
|
||||||
Mediator.Subscribe<SwitchToMainUiMessage>(this, (_) => IsOpen = true);
|
Mediator.Subscribe<SwitchToMainUiMessage>(this, (_) => IsOpen = true);
|
||||||
Mediator.Subscribe<SwitchToIntroUiMessage>(this, (_) => IsOpen = false);
|
Mediator.Subscribe<SwitchToIntroUiMessage>(this, (_) => IsOpen = false);
|
||||||
@@ -104,8 +104,8 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
protected override void DrawInternal()
|
protected override void DrawInternal()
|
||||||
{
|
{
|
||||||
if (_serverManager.CurrentApiUrl.Equals(ApiController.LoporritServiceUri, StringComparison.Ordinal))
|
if (_serverManager.CurrentApiUrl.Equals(ApiController.ClubPenguinServiceUri, StringComparison.Ordinal))
|
||||||
UiSharedService.AccentColor = new Vector4(1.0f, 0.8666f, 0.06666f, 1.0f);
|
UiSharedService.AccentColor = new Vector4(0.70196078431f, 0.54901960784f, 1.0f, 1.0f);
|
||||||
else
|
else
|
||||||
UiSharedService.AccentColor = ImGuiColors.ParsedGreen;
|
UiSharedService.AccentColor = ImGuiColors.ParsedGreen;
|
||||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().WindowPadding.Y - 1f * ImGuiHelpers.GlobalScale + ImGui.GetStyle().ItemSpacing.Y);
|
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ImGui.GetStyle().WindowPadding.Y - 1f * ImGuiHelpers.GlobalScale + ImGui.GetStyle().ItemSpacing.Y);
|
||||||
@@ -121,8 +121,8 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.AlignTextToFramePadding();
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.TextColored(ImGuiColors.DalamudRed, unsupported);
|
ImGui.TextColored(ImGuiColors.DalamudRed, unsupported);
|
||||||
}
|
}
|
||||||
UiSharedService.ColorTextWrapped($"Your Loporrit installation is out of date, the current version is {ver.Major}.{ver.Minor}.{ver.Build}. " +
|
UiSharedService.ColorTextWrapped($"Your Club Penguin Sync installation is out of date, the current version is {ver.Major}.{ver.Minor}.{ver.Build}. " +
|
||||||
$"It is highly recommended to keep Loporrit up to date. Open /xlplugins and update the plugin.", ImGuiColors.DalamudRed);
|
$"It is highly recommended to keep Club Penguin Sync up to date. Open /xlplugins and update the plugin.", ImGuiColors.DalamudRed);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (ImRaii.PushId("header")) DrawUIDHeader();
|
using (ImRaii.PushId("header")) DrawUIDHeader();
|
||||||
@@ -525,7 +525,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
Mediator.Publish(new OpenSettingsUiMessage());
|
Mediator.Publish(new OpenSettingsUiMessage());
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Open the Loporrit Settings");
|
UiSharedService.AttachToolTip("Open the Club Penguin Sync Settings");
|
||||||
|
|
||||||
ImGui.SameLine(); //Important to draw the uidText consistently
|
ImGui.SameLine(); //Important to draw the uidText consistently
|
||||||
ImGui.SetCursorPos(originalPos);
|
ImGui.SetCursorPos(originalPos);
|
||||||
|
|||||||
@@ -344,6 +344,7 @@ public class DrawGroupPair : DrawPairBase
|
|||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_pair.IsPaused)
|
if (!_pair.IsPaused)
|
||||||
{
|
{
|
||||||
if (_uiSharedService.IconTextButton(FontAwesomeIcon.User, "Open Profile"))
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.User, "Open Profile"))
|
||||||
@@ -352,6 +353,22 @@ public class DrawGroupPair : DrawPairBase
|
|||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isBlocked = _pair.IsApplicationBlocked;
|
||||||
|
bool isBlacklisted = _pair.IsBlacklisted;
|
||||||
|
bool isWhitelisted = _pair.IsWhitelisted;
|
||||||
|
|
||||||
|
if (!isBlocked && !isBlacklisted)
|
||||||
|
{
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.EyeSlash, "Block Modded Appearance"))
|
||||||
|
_pair.IsBlacklisted = true;
|
||||||
|
}
|
||||||
|
else if (isBlacklisted)
|
||||||
|
{
|
||||||
|
if (_uiSharedService.IconTextButton(FontAwesomeIcon.ThumbsUp, "Remove from Blacklist"))
|
||||||
|
_pair.IsBlacklisted = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (_pair.IsVisible)
|
if (_pair.IsVisible)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|||||||
@@ -165,10 +165,10 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
UiSharedService.DrawTree("What is this? (Explanation / Help)", () =>
|
UiSharedService.DrawTree("What is this? (Explanation / Help)", () =>
|
||||||
{
|
{
|
||||||
UiSharedService.TextWrapped("This tab allows you to see which transient files are attached to your character.");
|
UiSharedService.TextWrapped("This tab allows you to see which transient files are attached to your character.");
|
||||||
UiSharedService.TextWrapped("Transient files are files that cannot be resolved to your character permanently. Loporrit gathers these files in the background while you execute animations, VFX, sound effects, etc.");
|
UiSharedService.TextWrapped("Transient files are files that cannot be resolved to your character permanently. Club Penguin Sync gathers these files in the background while you execute animations, VFX, sound effects, etc.");
|
||||||
UiSharedService.TextWrapped("When sending your character data to others, Loporrit will combine the files listed in \"All Jobs\" and the corresponding currently used job.");
|
UiSharedService.TextWrapped("When sending your character data to others, Club Penguin Sync will combine the files listed in \"All Jobs\" and the corresponding currently used job.");
|
||||||
UiSharedService.TextWrapped("The purpose of this tab is primarily informational for you to see which files you are carrying with you. You can remove added game paths, however if you are using the animations etc. again, "
|
UiSharedService.TextWrapped("The purpose of this tab is primarily informational for you to see which files you are carrying with you. You can remove added game paths, however if you are using the animations etc. again, "
|
||||||
+ "Loporrit will automatically attach these after using them. If you disable associated mods in Penumbra, the associated entries here will also be deleted automatically.");
|
+ "Club Penguin Sync will automatically attach these after using them. If you disable associated mods in Penumbra, the associated entries here will also be deleted automatically.");
|
||||||
});
|
});
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
ImGuiHelpers.ScaledDummy(5);
|
||||||
@@ -365,7 +365,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
UiSharedService.TextWrapped("This tab allows you to attempt to fix mods that do not sync correctly, especially those with modded models and animations." + Environment.NewLine + Environment.NewLine
|
UiSharedService.TextWrapped("This tab allows you to attempt to fix mods that do not sync correctly, especially those with modded models and animations." + Environment.NewLine + Environment.NewLine
|
||||||
+ "To use this, start the recording, execute one or multiple emotes/animations you want to attempt to fix and check if new data appears in the table below." + Environment.NewLine
|
+ "To use this, start the recording, execute one or multiple emotes/animations you want to attempt to fix and check if new data appears in the table below." + Environment.NewLine
|
||||||
+ "If it doesn't, Loporrit is not able to catch the data or already has recorded the animation files (check 'Show previously added transient files' to see if not all is already present)." + Environment.NewLine + Environment.NewLine
|
+ "If it doesn't, Club Penguin Sync is not able to catch the data or already has recorded the animation files (check 'Show previously added transient files' to see if not all is already present)." + Environment.NewLine + Environment.NewLine
|
||||||
+ "For most animations, vfx, etc. it is enough to just run them once unless they have random variations. Longer animations do not require to play out in their entirety to be captured.");
|
+ "For most animations, vfx, etc. it is enough to just run them once unless they have random variations. Longer animations do not require to play out in their entirety to be captured.");
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
ImGuiHelpers.ScaledDummy(5);
|
||||||
UiSharedService.DrawGroupedCenteredColorText("Important Note: If you need to fix an animation that should apply across multiple jobs, you need to repeat this process with at least one additional job, " +
|
UiSharedService.DrawGroupedCenteredColorText("Important Note: If you need to fix an animation that should apply across multiple jobs, you need to repeat this process with at least one additional job, " +
|
||||||
@@ -405,7 +405,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
ImGuiHelpers.ScaledDummy(5);
|
||||||
ImGui.Checkbox("Show previously added transient files in the recording", ref _showAlreadyAddedTransients);
|
ImGui.Checkbox("Show previously added transient files in the recording", ref _showAlreadyAddedTransients);
|
||||||
_uiSharedService.DrawHelpText("Use this only if you want to see what was previously already caught by Loporrit");
|
_uiSharedService.DrawHelpText("Use this only if you want to see what was previously already caught by Club Penguin Sync");
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
ImGuiHelpers.ScaledDummy(5);
|
||||||
|
|
||||||
using (ImRaii.Disabled(_transientResourceManager.IsTransientRecording || _transientResourceManager.RecordedTransients.All(k => !k.AddTransient) || !_acknowledgeReview))
|
using (ImRaii.Disabled(_transientResourceManager.IsTransientRecording || _transientResourceManager.RecordedTransients.All(k => !k.AddTransient) || !_acknowledgeReview))
|
||||||
@@ -475,7 +475,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
UiSharedService.DrawTree("What is this? (Explanation / Help)", () =>
|
UiSharedService.DrawTree("What is this? (Explanation / Help)", () =>
|
||||||
{
|
{
|
||||||
UiSharedService.TextWrapped("This tab shows you all files and their sizes that are currently in use through your character and associated entities in Loporrit");
|
UiSharedService.TextWrapped("This tab shows you all files and their sizes that are currently in use through your character and associated entities in Club Penguin Sync");
|
||||||
});
|
});
|
||||||
|
|
||||||
if (_cachedAnalysis == null || _cachedAnalysis.Count == 0) return;
|
if (_cachedAnalysis == null || _cachedAnalysis.Count == 0) return;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
public DownloadUi(ILogger<DownloadUi> logger, DalamudUtilService dalamudUtilService, MareConfigService configService,
|
public DownloadUi(ILogger<DownloadUi> logger, DalamudUtilService dalamudUtilService, MareConfigService configService,
|
||||||
FileUploadManager fileTransferManager, MareMediator mediator, UiSharedService uiShared, PerformanceCollectorService performanceCollectorService)
|
FileUploadManager fileTransferManager, MareMediator mediator, UiSharedService uiShared, PerformanceCollectorService performanceCollectorService)
|
||||||
: base(logger, mediator, "Loporrit Downloads", performanceCollectorService)
|
: base(logger, mediator, "Club Penguin Sync Downloads", performanceCollectorService)
|
||||||
{
|
{
|
||||||
_dalamudUtilService = dalamudUtilService;
|
_dalamudUtilService = dalamudUtilService;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ public sealed class DtrEntry : IDisposable, IHostedService
|
|||||||
private IDtrBarEntry CreateEntry()
|
private IDtrBarEntry CreateEntry()
|
||||||
{
|
{
|
||||||
_logger.LogTrace("Creating new DtrBar entry");
|
_logger.LogTrace("Creating new DtrBar entry");
|
||||||
var entry = _dtrBar.Get("Loporrit");
|
var entry = _dtrBar.Get("ClubPenguinSync");
|
||||||
entry.OnClick = _ => _mareMediator.Publish(new UiToggleMessage(typeof(CompactUi)));
|
entry.OnClick = _ => _mareMediator.Publish(new UiToggleMessage(typeof(CompactUi)));
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
@@ -163,19 +163,19 @@ public sealed class DtrEntry : IDisposable, IHostedService
|
|||||||
.Select(x => string.Format("{0}", _configService.Current.PreferNoteInDtrTooltip ? x.GetNoteOrName() : x.PlayerName));
|
.Select(x => string.Format("{0}", _configService.Current.PreferNoteInDtrTooltip ? x.GetNoteOrName() : x.PlayerName));
|
||||||
}
|
}
|
||||||
|
|
||||||
tooltip = $"Loporrit: Connected{Environment.NewLine}----------{Environment.NewLine}{string.Join(Environment.NewLine, visiblePairs)}";
|
tooltip = $"ClubPenguinSync: Connected{Environment.NewLine}----------{Environment.NewLine}{string.Join(Environment.NewLine, visiblePairs)}";
|
||||||
colors = _configService.Current.DtrColorsPairsInRange;
|
colors = _configService.Current.DtrColorsPairsInRange;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tooltip = "Loporrit: Connected";
|
tooltip = "ClubPenguinSync: Connected";
|
||||||
colors = _configService.Current.DtrColorsDefault;
|
colors = _configService.Current.DtrColorsDefault;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
text = RenderDtrStyle(_configService.Current.DtrStyle, "\uE04C");
|
text = RenderDtrStyle(_configService.Current.DtrStyle, "\uE04C");
|
||||||
tooltip = "Loporrit: Not Connected";
|
tooltip = "ClubPenguinSync: Not Connected";
|
||||||
colors = _configService.Current.DtrColorsNotConnected;
|
colors = _configService.Current.DtrColorsNotConnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
ApiController apiController, UiSharedService uiSharedService, FileDialogManager fileDialogManager,
|
ApiController apiController, UiSharedService uiSharedService, FileDialogManager fileDialogManager,
|
||||||
ServerConfigurationManager serverConfigurationManager,
|
ServerConfigurationManager serverConfigurationManager,
|
||||||
MareProfileManager mareProfileManager, PerformanceCollectorService performanceCollectorService)
|
MareProfileManager mareProfileManager, PerformanceCollectorService performanceCollectorService)
|
||||||
: base(logger, mediator, "Loporrit Edit Profile###LoporritSyncEditProfileUI", performanceCollectorService)
|
: base(logger, mediator, "Club Penguin Sync Edit Profile###ClubPenguinSyncEditProfileUI", performanceCollectorService)
|
||||||
{
|
{
|
||||||
IsOpen = false;
|
IsOpen = false;
|
||||||
this.SizeConstraints = new()
|
this.SizeConstraints = new()
|
||||||
@@ -136,7 +136,7 @@ public class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
using MemoryStream ms = new(fileContent);
|
using MemoryStream ms = new(fileContent);
|
||||||
var format = PngHdr.TryExtractDimensions(ms);
|
var format = PngHdr.TryExtractDimensions(ms);
|
||||||
|
|
||||||
if (format.Width > 256 || format.Height > 256 || (fileContent.Length > 250 * 1024))
|
if (format.Width > 257 || format.Height > 257 || (fileContent.Length > 250 * 1024))
|
||||||
{
|
{
|
||||||
_showFileDialogError = true;
|
_showFileDialogError = true;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public partial class IntroUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
public IntroUi(ILogger<IntroUi> logger, UiSharedService uiShared, MareConfigService configService,
|
public IntroUi(ILogger<IntroUi> logger, UiSharedService uiShared, MareConfigService configService,
|
||||||
CacheMonitor fileCacheManager, ServerConfigurationManager serverConfigurationManager, MareMediator mareMediator,
|
CacheMonitor fileCacheManager, ServerConfigurationManager serverConfigurationManager, MareMediator mareMediator,
|
||||||
PerformanceCollectorService performanceCollectorService, DalamudUtilService dalamudUtilService, AccountRegistrationService registerService) : base(logger, mareMediator, "Loporrit Setup", performanceCollectorService)
|
PerformanceCollectorService performanceCollectorService, DalamudUtilService dalamudUtilService, AccountRegistrationService registerService) : base(logger, mareMediator, "Club Penguin Sync Setup", performanceCollectorService)
|
||||||
{
|
{
|
||||||
_uiShared = uiShared;
|
_uiShared = uiShared;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
@@ -108,9 +108,9 @@ public partial class IntroUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
if (!_configService.Current.AcceptedAgreement && !_readFirstPage)
|
if (!_configService.Current.AcceptedAgreement && !_readFirstPage)
|
||||||
{
|
{
|
||||||
_uiShared.BigText("Welcome to Loporrit");
|
_uiShared.BigText("Welcome to Club Penguin Sync");
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
UiSharedService.TextWrapped("Loporrit is a plugin that will replicate your full current character state including all Penumbra mods to other paired users. " +
|
UiSharedService.TextWrapped("Club Penguin Sync is a plugin that will replicate your full current character state including all Penumbra mods to other paired users. " +
|
||||||
"Note that you will have to have Penumbra as well as Glamourer installed to use this plugin.");
|
"Note that you will have to have Penumbra as well as Glamourer installed to use this plugin.");
|
||||||
UiSharedService.TextWrapped("We will have to setup a few things first before you can start using this plugin. Click on next to continue.");
|
UiSharedService.TextWrapped("We will have to setup a few things first before you can start using this plugin. Click on next to continue.");
|
||||||
|
|
||||||
@@ -201,11 +201,11 @@ This service is provided as-is.
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UiSharedService.TextWrapped("To not unnecessary download files already present on your computer, Loporrit will have to scan your Penumbra mod directory. " +
|
UiSharedService.TextWrapped("To not unnecessary download files already present on your computer, Club Penguin Sync will have to scan your Penumbra mod directory. " +
|
||||||
"Additionally, a local storage folder must be set where Loporrit will download other character files to. " +
|
"Additionally, a local storage folder must be set where Club Penguin Sync will download other character files to. " +
|
||||||
"Once the storage folder is set and the scan complete, this page will automatically forward to registration at a service.");
|
"Once the storage folder is set and the scan complete, this page will automatically forward to registration at a service.");
|
||||||
UiSharedService.TextWrapped("Note: The initial scan, depending on the amount of mods you have, might take a while. Please wait until it is completed.");
|
UiSharedService.TextWrapped("Note: The initial scan, depending on the amount of mods you have, might take a while. Please wait until it is completed.");
|
||||||
UiSharedService.ColorTextWrapped("Warning: once past this step you should not delete the FileCache.csv of Loporrit in the Plugin Configurations folder of Dalamud. " +
|
UiSharedService.ColorTextWrapped("Warning: once past this step you should not delete the FileCache.csv of Club Penguin Sync in the Plugin Configurations folder of Dalamud. " +
|
||||||
"Otherwise on the next launch a full re-scan of the file cache database will be initiated.", ImGuiColors.DalamudYellow);
|
"Otherwise on the next launch a full re-scan of the file cache database will be initiated.", ImGuiColors.DalamudYellow);
|
||||||
UiSharedService.ColorTextWrapped("Warning: if the scan is hanging and does nothing for a long time, chances are high your Penumbra folder is not set up properly.", ImGuiColors.DalamudYellow);
|
UiSharedService.ColorTextWrapped("Warning: if the scan is hanging and does nothing for a long time, chances are high your Penumbra folder is not set up properly.", ImGuiColors.DalamudYellow);
|
||||||
_uiShared.DrawCacheDirectorySetting();
|
_uiShared.DrawCacheDirectorySetting();
|
||||||
@@ -230,8 +230,8 @@ This service is provided as-is.
|
|||||||
_configService.Current.UseCompactor = useFileCompactor;
|
_configService.Current.UseCompactor = useFileCompactor;
|
||||||
_configService.Save();
|
_configService.Save();
|
||||||
}
|
}
|
||||||
UiSharedService.ColorTextWrapped("The File Compactor can save a tremendeous amount of space on the hard disk for downloads through Loporrit. It will incur a minor CPU penalty on download but can speed up " +
|
UiSharedService.ColorTextWrapped("The File Compactor can save a tremendeous amount of space on the hard disk for downloads through Club Penguin Sync. It will incur a minor CPU penalty on download but can speed up " +
|
||||||
"loading of other characters. It is recommended to keep it enabled. You can change this setting later anytime in the Loporrit settings.", ImGuiColors.DalamudYellow);
|
"loading of other characters. It is recommended to keep it enabled. You can change this setting later anytime in the Club Penguin Sync settings.", ImGuiColors.DalamudYellow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!_uiShared.ApiController.IsConnected)
|
else if (!_uiShared.ApiController.IsConnected)
|
||||||
@@ -239,7 +239,7 @@ This service is provided as-is.
|
|||||||
using (_uiShared.UidFont.Push())
|
using (_uiShared.UidFont.Push())
|
||||||
ImGui.TextUnformatted("Service Registration");
|
ImGui.TextUnformatted("Service Registration");
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
UiSharedService.TextWrapped("To be able to use Loporrit you will have to register an account.");
|
UiSharedService.TextWrapped("To be able to use Club Penguin Sync you will have to register an account.");
|
||||||
UiSharedService.TextWrapped("Refer to the instructions at the location you obtained this plugin for more information or support.");
|
UiSharedService.TextWrapped("Refer to the instructions at the location you obtained this plugin for more information or support.");
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
@@ -247,12 +247,12 @@ This service is provided as-is.
|
|||||||
ImGui.BeginDisabled(_registrationInProgress || _uiShared.ApiController.ServerState == ServerState.Connecting || _uiShared.ApiController.ServerState == ServerState.Reconnecting);
|
ImGui.BeginDisabled(_registrationInProgress || _uiShared.ApiController.ServerState == ServerState.Connecting || _uiShared.ApiController.ServerState == ServerState.Reconnecting);
|
||||||
_ = _uiShared.DrawServiceSelection(selectOnChange: true, intro: true);
|
_ = _uiShared.DrawServiceSelection(selectOnChange: true, intro: true);
|
||||||
|
|
||||||
if (true) // Enable registration button for all servers
|
if (_serverConfigurationManager.CurrentApiUrl == null || !_serverConfigurationManager.CurrentApiUrl.Equals(ApiController.ClubPenguinServiceUri, StringComparison.Ordinal)) // Enable registration button for all servers
|
||||||
{
|
{
|
||||||
ImGui.BeginDisabled(_registrationInProgress || _registrationSuccess || _secretKey.Length > 0);
|
ImGui.BeginDisabled(_registrationInProgress || _registrationSuccess || _secretKey.Length > 0);
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
ImGui.TextUnformatted("If you have not used Loporrit before, click below to register a new account.");
|
ImGui.TextUnformatted("If you have not used Club Penguin Sync before, click below to register a new account.");
|
||||||
if (_uiShared.IconTextButton(FontAwesomeIcon.Plus, "Register a new Loporrit account"))
|
if (_uiShared.IconTextButton(FontAwesomeIcon.Plus, "Register a new Club Penguin Sync account"))
|
||||||
{
|
{
|
||||||
_registrationInProgress = true;
|
_registrationInProgress = true;
|
||||||
_ = Task.Run(async () => {
|
_ = Task.Run(async () => {
|
||||||
@@ -297,6 +297,12 @@ This service is provided as-is.
|
|||||||
ImGui.TextWrapped(_registrationMessage);
|
ImGui.TextWrapped(_registrationMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.Separator();
|
||||||
|
UiSharedService.TextWrapped("You must join the discord to register a Club Penguin Sync account on the main server.");
|
||||||
|
UiSharedService.TextWrapped("Use the /signup command to register an account within the discord follow the instructiosn in the #how-to-setup channel.");
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class PermissionWindowUI : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
public PermissionWindowUI(ILogger<PermissionWindowUI> logger, Pair pair, MareMediator mediator, UiSharedService uiSharedService,
|
public PermissionWindowUI(ILogger<PermissionWindowUI> logger, Pair pair, MareMediator mediator, UiSharedService uiSharedService,
|
||||||
ApiController apiController, PerformanceCollectorService performanceCollectorService)
|
ApiController apiController, PerformanceCollectorService performanceCollectorService)
|
||||||
: base(logger, mediator, "Permissions for " + pair.UserData.AliasOrUID + "###LoporritSyncPermissions" + pair.UserData.UID, performanceCollectorService)
|
: base(logger, mediator, "Permissions for " + pair.UserData.AliasOrUID + "###ClubPenguinSyncPermissions" + pair.UserData.UID, performanceCollectorService)
|
||||||
{
|
{
|
||||||
Pair = pair;
|
Pair = pair;
|
||||||
_uiSharedService = uiSharedService;
|
_uiSharedService = uiSharedService;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class PlayerAnalysisUI : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
public PlayerAnalysisUI(ILogger<PlayerAnalysisUI> logger, Pair pair, MareMediator mediator, UiSharedService uiSharedService,
|
public PlayerAnalysisUI(ILogger<PlayerAnalysisUI> logger, Pair pair, MareMediator mediator, UiSharedService uiSharedService,
|
||||||
PerformanceCollectorService performanceCollectorService)
|
PerformanceCollectorService performanceCollectorService)
|
||||||
: base(logger, mediator, "Character Data Analysis for " + pair.UserData.AliasOrUID + "###LoporritPairAnalysis" + pair.UserData.UID, performanceCollectorService)
|
: base(logger, mediator, "Character Data Analysis for " + pair.UserData.AliasOrUID + "###ClubPenguinSyncPairAnalysis" + pair.UserData.UID, performanceCollectorService)
|
||||||
{
|
{
|
||||||
Pair = pair;
|
Pair = pair;
|
||||||
_uiSharedService = uiSharedService;
|
_uiSharedService = uiSharedService;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class PopoutProfileUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
public PopoutProfileUi(ILogger<PopoutProfileUi> logger, MareMediator mediator, UiSharedService uiSharedService,
|
public PopoutProfileUi(ILogger<PopoutProfileUi> logger, MareMediator mediator, UiSharedService uiSharedService,
|
||||||
ServerConfigurationManager serverManager, MareConfigService mareConfigService,
|
ServerConfigurationManager serverManager, MareConfigService mareConfigService,
|
||||||
MareProfileManager mareProfileManager, PairManager pairManager, PerformanceCollectorService performanceCollectorService) : base(logger, mediator, "###LoporritSyncPopoutProfileUI", performanceCollectorService)
|
MareProfileManager mareProfileManager, PairManager pairManager, PerformanceCollectorService performanceCollectorService) : base(logger, mediator, "###ClubPenguinSyncPopoutProfileUI", performanceCollectorService)
|
||||||
{
|
{
|
||||||
_uiSharedService = uiSharedService;
|
_uiSharedService = uiSharedService;
|
||||||
_serverManager = serverManager;
|
_serverManager = serverManager;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using Dalamud.Utility;
|
|||||||
using MareSynchronos.API.Data;
|
using MareSynchronos.API.Data;
|
||||||
using MareSynchronos.API.Data.Comparer;
|
using MareSynchronos.API.Data.Comparer;
|
||||||
using MareSynchronos.FileCache;
|
using MareSynchronos.FileCache;
|
||||||
|
using MareSynchronos.Interop;
|
||||||
using MareSynchronos.Interop.Ipc;
|
using MareSynchronos.Interop.Ipc;
|
||||||
using MareSynchronos.MareConfiguration;
|
using MareSynchronos.MareConfiguration;
|
||||||
using MareSynchronos.MareConfiguration.Models;
|
using MareSynchronos.MareConfiguration.Models;
|
||||||
@@ -22,8 +23,10 @@ using MareSynchronos.WebAPI.Files.Models;
|
|||||||
using MareSynchronos.WebAPI.SignalR.Utils;
|
using MareSynchronos.WebAPI.SignalR.Utils;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace MareSynchronos.UI;
|
namespace MareSynchronos.UI;
|
||||||
@@ -49,6 +52,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
private readonly PlayerPerformanceService _playerPerformanceService;
|
private readonly PlayerPerformanceService _playerPerformanceService;
|
||||||
private readonly AccountRegistrationService _registerService;
|
private readonly AccountRegistrationService _registerService;
|
||||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||||
|
private readonly ColorTableHook _colorTableHook;
|
||||||
private readonly UiSharedService _uiShared;
|
private readonly UiSharedService _uiShared;
|
||||||
private bool _deleteAccountPopupModalShown = false;
|
private bool _deleteAccountPopupModalShown = false;
|
||||||
private string _lastTab = string.Empty;
|
private string _lastTab = string.Empty;
|
||||||
@@ -76,11 +80,12 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
FileCacheManager fileCacheManager,
|
FileCacheManager fileCacheManager,
|
||||||
FileCompactor fileCompactor, ApiController apiController,
|
FileCompactor fileCompactor, ApiController apiController,
|
||||||
IpcManager ipcManager, IpcProvider ipcProvider, CacheMonitor cacheMonitor,
|
IpcManager ipcManager, IpcProvider ipcProvider, CacheMonitor cacheMonitor,
|
||||||
DalamudUtilService dalamudUtilService, AccountRegistrationService registerService) : base(logger, mediator, "Loporrit Settings", performanceCollector)
|
DalamudUtilService dalamudUtilService, AccountRegistrationService registerService, ColorTableHook colorTableHook) : base(logger, mediator, "Club Penguin Sync Settings", performanceCollector)
|
||||||
{
|
{
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_pairManager = pairManager;
|
_pairManager = pairManager;
|
||||||
_chatService = chatService;
|
_chatService = chatService;
|
||||||
|
_colorTableHook = colorTableHook;
|
||||||
_guiHookService = guiHookService;
|
_guiHookService = guiHookService;
|
||||||
_serverConfigurationManager = serverConfigurationManager;
|
_serverConfigurationManager = serverConfigurationManager;
|
||||||
_playerPerformanceConfigService = playerPerformanceConfigService;
|
_playerPerformanceConfigService = playerPerformanceConfigService;
|
||||||
@@ -580,36 +585,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
_uiShared.BigText("Advanced");
|
_uiShared.BigText("Advanced");
|
||||||
|
|
||||||
bool mareApi = _configService.Current.MareAPI;
|
|
||||||
if (ImGui.Checkbox("Enable Mare Synchronos API", ref mareApi))
|
|
||||||
{
|
|
||||||
_configService.Current.MareAPI = mareApi;
|
|
||||||
_configService.Save();
|
|
||||||
_ipcProvider.HandleMareImpersonation();
|
|
||||||
}
|
|
||||||
_uiShared.DrawHelpText("Enables handling of the Mare Synchronos API. This currently includes:\n\n" +
|
|
||||||
" - MCDF loading support for other plugins\n" +
|
|
||||||
" - Blocking Moodles applications to paired users\n\n" +
|
|
||||||
"If the Mare Synchronos plugin is loaded while this option is enabled, control of its API will be relinquished.");
|
|
||||||
|
|
||||||
using (_ = ImRaii.PushIndent())
|
|
||||||
{
|
|
||||||
ImGui.SameLine(300.0f * ImGuiHelpers.GlobalScale);
|
|
||||||
if (_ipcProvider.ImpersonationActive)
|
|
||||||
{
|
|
||||||
UiSharedService.ColorTextWrapped("Mare API active!", ImGuiColors.HealerGreen);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!mareApi)
|
|
||||||
UiSharedService.ColorTextWrapped("Mare API inactive: Option is disabled", ImGuiColors.DalamudYellow);
|
|
||||||
else if (_ipcProvider.MarePluginEnabled)
|
|
||||||
UiSharedService.ColorTextWrapped("Mare API inactive: Mare plugin is loaded", ImGuiColors.DalamudYellow);
|
|
||||||
else
|
|
||||||
UiSharedService.ColorTextWrapped("Mare API inactive: Unknown reason", ImGuiColors.DalamudRed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool logEvents = _configService.Current.LogEvents;
|
bool logEvents = _configService.Current.LogEvents;
|
||||||
if (ImGui.Checkbox("Log Event Viewer data to disk", ref logEvents))
|
if (ImGui.Checkbox("Log Event Viewer data to disk", ref logEvents))
|
||||||
{
|
{
|
||||||
@@ -632,6 +607,22 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
_configService.Save();
|
_configService.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool attemptColorTableProtection = _configService.Current.AttemptColorTableProtection;
|
||||||
|
if (ImGui.Checkbox("[EXPERIMENTAL] Attempt to fix potential ColorTable crashes.", ref attemptColorTableProtection))
|
||||||
|
{
|
||||||
|
if (attemptColorTableProtection)
|
||||||
|
{
|
||||||
|
_colorTableHook.EnableHooks();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_colorTableHook.DisableHooks();
|
||||||
|
}
|
||||||
|
//this shouldnt need the mediator as this is the ONLY source of truth.
|
||||||
|
_configService.Current.AttemptColorTableProtection = attemptColorTableProtection;
|
||||||
|
_configService.Save();
|
||||||
|
}
|
||||||
|
|
||||||
bool serializedApplications = _configService.Current.SerialApplication;
|
bool serializedApplications = _configService.Current.SerialApplication;
|
||||||
if (ImGui.Checkbox("Serialized player applications", ref serializedApplications))
|
if (ImGui.Checkbox("Serialized player applications", ref serializedApplications))
|
||||||
{
|
{
|
||||||
@@ -672,6 +663,26 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
_configService.Save();
|
_configService.Save();
|
||||||
}, _configService.Current.LogLevel);
|
}, _configService.Current.LogLevel);
|
||||||
|
|
||||||
|
bool logTraceToFile = _configService.Current.LogTraceLog;
|
||||||
|
if (ImGui.Checkbox("Enable Trace Logging to File", ref logTraceToFile))
|
||||||
|
{
|
||||||
|
_configService.Current.LogTraceLog = logTraceToFile;
|
||||||
|
_configService.Save();
|
||||||
|
ClubPenguinSync.Plugin.Self.ToggleFileLogging(_configService.Current.LogTraceLog, "Setting Change");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SameLine(300.0f * ImGuiHelpers.GlobalScale);
|
||||||
|
if (_uiShared.IconTextButton(FontAwesomeIcon.FolderOpen, "Open TraceLog folder"))
|
||||||
|
{
|
||||||
|
ProcessStartInfo ps = new()
|
||||||
|
{
|
||||||
|
FileName = Path.Combine(_configService.ConfigurationDirectory, "tracelog"),
|
||||||
|
UseShellExecute = true,
|
||||||
|
WindowStyle = ProcessWindowStyle.Normal
|
||||||
|
};
|
||||||
|
Process.Start(ps);
|
||||||
|
}
|
||||||
|
|
||||||
bool logPerformance = _configService.Current.LogPerformance;
|
bool logPerformance = _configService.Current.LogPerformance;
|
||||||
if (ImGui.Checkbox("Log Performance Counters", ref logPerformance))
|
if (ImGui.Checkbox("Log Performance Counters", ref logPerformance))
|
||||||
{
|
{
|
||||||
@@ -728,7 +739,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
_uiShared.BigText("Storage");
|
_uiShared.BigText("Storage");
|
||||||
|
|
||||||
UiSharedService.TextWrapped("Loporrit stores downloaded files from paired people permanently. This is to improve loading performance and requiring less downloads. " +
|
UiSharedService.TextWrapped("Club Penguin Sync stores downloaded files from paired people permanently. This is to improve loading performance and requiring less downloads. " +
|
||||||
"The storage governs itself by clearing data beyond the set storage size. Please set the storage size accordingly. It is not necessary to manually clear the storage.");
|
"The storage governs itself by clearing data beyond the set storage size. Please set the storage size accordingly. It is not necessary to manually clear the storage.");
|
||||||
|
|
||||||
_uiShared.DrawFileScanState();
|
_uiShared.DrawFileScanState();
|
||||||
@@ -745,7 +756,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui.AlignTextToFramePadding();
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.TextUnformatted("Monitoring Loporrit Storage Folder: " + (_cacheMonitor.MareWatcher?.Path ?? "Not monitoring"));
|
ImGui.TextUnformatted("Monitoring Club Penguin Sync Storage Folder: " + (_cacheMonitor.MareWatcher?.Path ?? "Not monitoring"));
|
||||||
if (string.IsNullOrEmpty(_cacheMonitor.MareWatcher?.Path))
|
if (string.IsNullOrEmpty(_cacheMonitor.MareWatcher?.Path))
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
@@ -763,7 +774,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
_cacheMonitor.StartPenumbraWatcher(_ipcManager.Penumbra.ModDirectory);
|
_cacheMonitor.StartPenumbraWatcher(_ipcManager.Penumbra.ModDirectory);
|
||||||
_cacheMonitor.InvokeScan();
|
_cacheMonitor.InvokeScan();
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Attempts to resume monitoring for both Penumbra and Loporrit Storage. "
|
UiSharedService.AttachToolTip("Attempts to resume monitoring for both Penumbra and Club Penguin Sync Storage. "
|
||||||
+ "Resuming the monitoring will also force a full scan to run." + Environment.NewLine
|
+ "Resuming the monitoring will also force a full scan to run." + Environment.NewLine
|
||||||
+ "If the button remains present after clicking it, consult /xllog for errors");
|
+ "If the button remains present after clicking it, consult /xllog for errors");
|
||||||
}
|
}
|
||||||
@@ -776,8 +787,8 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
_cacheMonitor.StopMonitoring();
|
_cacheMonitor.StopMonitoring();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Stops the monitoring for both Penumbra and Loporrit Storage. "
|
UiSharedService.AttachToolTip("Stops the monitoring for both Penumbra and Club Penguin Sync Storage. "
|
||||||
+ "Do not stop the monitoring, unless you plan to move the Penumbra and Loporrit Storage folders, to ensure correct functionality of Loporrit." + Environment.NewLine
|
+ "Do not stop the monitoring, unless you plan to move the Penumbra and Club Penguin Sync Storage folders, to ensure correct functionality of Club Penguin Sync." + Environment.NewLine
|
||||||
+ "If you stop the monitoring to move folders around, resume it after you are finished moving the files."
|
+ "If you stop the monitoring to move folders around, resume it after you are finished moving the files."
|
||||||
+ UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
|
+ UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
|
||||||
}
|
}
|
||||||
@@ -794,7 +805,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
bool useFileCompactor = _configService.Current.UseCompactor;
|
bool useFileCompactor = _configService.Current.UseCompactor;
|
||||||
if (!useFileCompactor && !isLinux)
|
if (!useFileCompactor && !isLinux)
|
||||||
{
|
{
|
||||||
UiSharedService.ColorTextWrapped("Hint: To free up space when using Loporrit consider enabling the File Compactor", ImGuiColors.DalamudYellow);
|
UiSharedService.ColorTextWrapped("Hint: To free up space when using Club Penguin Sync consider enabling the File Compactor", ImGuiColors.DalamudYellow);
|
||||||
}
|
}
|
||||||
if (isLinux || !_cacheMonitor.StorageisNTFS) ImGui.BeginDisabled();
|
if (isLinux || !_cacheMonitor.StorageisNTFS) ImGui.BeginDisabled();
|
||||||
if (ImGui.Checkbox("Use file compactor", ref useFileCompactor))
|
if (ImGui.Checkbox("Use file compactor", ref useFileCompactor))
|
||||||
@@ -903,7 +914,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("You normally do not need to do this. THIS IS NOT SOMETHING YOU SHOULD BE DOING TO TRY TO FIX SYNC ISSUES." + Environment.NewLine
|
UiSharedService.AttachToolTip("You normally do not need to do this. THIS IS NOT SOMETHING YOU SHOULD BE DOING TO TRY TO FIX SYNC ISSUES." + Environment.NewLine
|
||||||
+ "This will solely remove all downloaded data from all players and will require you to re-download everything again." + Environment.NewLine
|
+ "This will solely remove all downloaded data from all players and will require you to re-download everything again." + Environment.NewLine
|
||||||
+ "Loporrit's storage is self-clearing and will not surpass the limit you have set it to." + Environment.NewLine
|
+ "Club Penguin Sync's storage is self-clearing and will not surpass the limit you have set it to." + Environment.NewLine
|
||||||
+ "If you still think you need to do this hold CTRL while pressing the button.");
|
+ "If you still think you need to do this hold CTRL while pressing the button.");
|
||||||
if (!_readClearCache)
|
if (!_readClearCache)
|
||||||
ImGui.EndDisabled();
|
ImGui.EndDisabled();
|
||||||
@@ -975,14 +986,14 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
_configService.Current.EnableRightClickMenus = enableRightClickMenu;
|
_configService.Current.EnableRightClickMenus = enableRightClickMenu;
|
||||||
_configService.Save();
|
_configService.Save();
|
||||||
}
|
}
|
||||||
_uiShared.DrawHelpText("This will add Loporrit related right click menu entries in the game UI on paired players.");
|
_uiShared.DrawHelpText("This will add Club Penguin Sync related right click menu entries in the game UI on paired players.");
|
||||||
|
|
||||||
if (ImGui.Checkbox("Display status and visible pair count in Server Info Bar", ref enableDtrEntry))
|
if (ImGui.Checkbox("Display status and visible pair count in Server Info Bar", ref enableDtrEntry))
|
||||||
{
|
{
|
||||||
_configService.Current.EnableDtrEntry = enableDtrEntry;
|
_configService.Current.EnableDtrEntry = enableDtrEntry;
|
||||||
_configService.Save();
|
_configService.Save();
|
||||||
}
|
}
|
||||||
_uiShared.DrawHelpText("This will add Loporrit connection status and visible pair count in the Server Info Bar.\nYou can further configure this through your Dalamud Settings.");
|
_uiShared.DrawHelpText("This will add Club Penguin Sync connection status and visible pair count in the Server Info Bar.\nYou can further configure this through your Dalamud Settings.");
|
||||||
|
|
||||||
using (ImRaii.Disabled(!enableDtrEntry))
|
using (ImRaii.Disabled(!enableDtrEntry))
|
||||||
{
|
{
|
||||||
@@ -1744,7 +1755,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
if (true) // Enable registration button for all servers
|
if (true) // Enable registration button for all servers
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (_uiShared.IconTextButton(FontAwesomeIcon.Plus, "Register a new Loporrit account"))
|
if (_uiShared.IconTextButton(FontAwesomeIcon.Plus, "Register a new Club Penguin Sync account"))
|
||||||
{
|
{
|
||||||
_registrationInProgress = true;
|
_registrationInProgress = true;
|
||||||
_ = Task.Run(async () => {
|
_ = Task.Run(async () => {
|
||||||
@@ -1800,7 +1811,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
var serverName = selectedServer.ServerName;
|
var serverName = selectedServer.ServerName;
|
||||||
var serverUri = selectedServer.ServerUri;
|
var serverUri = selectedServer.ServerUri;
|
||||||
var isMain = string.Equals(serverName, ApiController.LoporritServer, StringComparison.OrdinalIgnoreCase);
|
var isMain = string.Equals(serverName, ApiController.ClubPenguinServer, StringComparison.OrdinalIgnoreCase);
|
||||||
var flags = isMain ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None;
|
var flags = isMain ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None;
|
||||||
|
|
||||||
if (ImGui.InputText("Service URI", ref serverUri, 255, flags))
|
if (ImGui.InputText("Service URI", ref serverUri, 255, flags))
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase
|
|||||||
public StandaloneProfileUi(ILogger<StandaloneProfileUi> logger, MareMediator mediator, UiSharedService uiBuilder,
|
public StandaloneProfileUi(ILogger<StandaloneProfileUi> logger, MareMediator mediator, UiSharedService uiBuilder,
|
||||||
ServerConfigurationManager serverManager, MareProfileManager mareProfileManager, PairManager pairManager, Pair pair,
|
ServerConfigurationManager serverManager, MareProfileManager mareProfileManager, PairManager pairManager, Pair pair,
|
||||||
PerformanceCollectorService performanceCollector)
|
PerformanceCollectorService performanceCollector)
|
||||||
: base(logger, mediator, "Profile of " + pair.UserData.AliasOrUID + "##LoporritSyncStandaloneProfileUI" + pair.UserData.AliasOrUID, performanceCollector)
|
: base(logger, mediator, "Profile of " + pair.UserData.AliasOrUID + "##ClubPenguinSyncStandaloneProfileUI" + pair.UserData.AliasOrUID, performanceCollector)
|
||||||
{
|
{
|
||||||
_uiSharedService = uiBuilder;
|
_uiSharedService = uiBuilder;
|
||||||
_serverManager = serverManager;
|
_serverManager = serverManager;
|
||||||
|
|||||||
@@ -535,7 +535,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
public void DrawCacheDirectorySetting()
|
public void DrawCacheDirectorySetting()
|
||||||
{
|
{
|
||||||
ColorTextWrapped("Note: The storage folder should be somewhere close to root (i.e. C:\\LoporritStorage) in a new empty folder. DO NOT point this to your game folder. DO NOT point this to your Penumbra folder.", ImGuiColors.DalamudYellow);
|
ColorTextWrapped("Note: The storage folder should be somewhere close to root (i.e. C:\\ClubPenguinSyncStorage) in a new empty folder. DO NOT point this to your game folder. DO NOT point this to your Penumbra folder.", ImGuiColors.DalamudYellow);
|
||||||
var cacheDirectory = _configService.Current.CacheFolder;
|
var cacheDirectory = _configService.Current.CacheFolder;
|
||||||
ImGui.SetNextItemWidth(400 * ImGuiHelpers.GlobalScale);
|
ImGui.SetNextItemWidth(400 * ImGuiHelpers.GlobalScale);
|
||||||
ImGui.InputText("Storage Folder##cache", ref cacheDirectory, 255, ImGuiInputTextFlags.ReadOnly);
|
ImGui.InputText("Storage Folder##cache", ref cacheDirectory, 255, ImGuiInputTextFlags.ReadOnly);
|
||||||
@@ -545,7 +545,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
if (IconButton(FontAwesomeIcon.Folder))
|
if (IconButton(FontAwesomeIcon.Folder))
|
||||||
{
|
{
|
||||||
FileDialogManager.OpenFolderDialog("Pick Loporrit Storage Folder", (success, path) =>
|
FileDialogManager.OpenFolderDialog("Pick Club Penguin Sync Storage Folder", (success, path) =>
|
||||||
{
|
{
|
||||||
if (!success) return;
|
if (!success) return;
|
||||||
|
|
||||||
@@ -605,7 +605,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
else if (_cacheDirectoryHasOtherFilesThanCache)
|
else if (_cacheDirectoryHasOtherFilesThanCache)
|
||||||
{
|
{
|
||||||
ColorTextWrapped("Your selected directory has files or directories inside that are not Loporrit related. Use an empty directory or a previous storage directory only.", ImGuiColors.DalamudRed);
|
ColorTextWrapped("Your selected directory has files or directories inside that are not Club Penguin Sync related. Use an empty directory or a previous storage directory only.", ImGuiColors.DalamudRed);
|
||||||
}
|
}
|
||||||
else if (!_cacheDirectoryIsValidPath)
|
else if (!_cacheDirectoryIsValidPath)
|
||||||
{
|
{
|
||||||
@@ -620,7 +620,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
_configService.Current.MaxLocalCacheInGiB = maxCacheSize;
|
_configService.Current.MaxLocalCacheInGiB = maxCacheSize;
|
||||||
_configService.Save();
|
_configService.Save();
|
||||||
}
|
}
|
||||||
DrawHelpText("The storage is automatically governed by Loporrit. It will clear itself automatically once it reaches the set capacity by removing the oldest unused files. You typically do not need to clear it yourself.");
|
DrawHelpText("The storage is automatically governed by Club Penguin Sync. It will clear itself automatically once it reaches the set capacity by removing the oldest unused files. You typically do not need to clear it yourself.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public T? DrawCombo<T>(string comboName, IEnumerable<T> comboItems, Func<T, string> toName,
|
public T? DrawCombo<T>(string comboName, IEnumerable<T> comboItems, Func<T, string> toName,
|
||||||
@@ -849,17 +849,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
if (!_penumbraExists || !_glamourerExists)
|
if (!_penumbraExists || !_glamourerExists)
|
||||||
{
|
{
|
||||||
ImGui.TextColored(ImGuiColors.DalamudRed, "You need to install both Penumbra and Glamourer and keep them up to date to use Loporrit.");
|
ImGui.TextColored(ImGuiColors.DalamudRed, "You need to install both Penumbra and Glamourer and keep them up to date to use Club Penguin Sync.");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (NoSnapService.AnyLoaded)
|
|
||||||
{
|
|
||||||
IconText(FontAwesomeIcon.ExclamationTriangle, ImGuiColors.DalamudYellow);
|
|
||||||
ImGui.SameLine();
|
|
||||||
var cursorX = ImGui.GetCursorPosX();
|
|
||||||
ImGui.TextColored(ImGuiColors.DalamudYellow, "Synced player appearances will not apply until incompatible plugins are disabled:");
|
|
||||||
ImGui.SetCursorPosX(cursorX + 16.0f);
|
|
||||||
ImGui.TextColored(ImGuiColors.DalamudYellow, NoSnapService.ActivePlugins);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ public static class ChatUtils
|
|||||||
{
|
{
|
||||||
var gidBytes = UTF8Encoding.UTF8.GetBytes(gid);
|
var gidBytes = UTF8Encoding.UTF8.GetBytes(gid);
|
||||||
var hashedBytes = MD5.HashData(gidBytes);
|
var hashedBytes = MD5.HashData(gidBytes);
|
||||||
|
for (int i = 0; i < hashedBytes.Length; ++i)
|
||||||
|
hashedBytes[i] ^= 0x01;
|
||||||
var guid = new Guid(hashedBytes);
|
var guid = new Guid(hashedBytes);
|
||||||
return CreateExtraChatTagPayload(guid);
|
return CreateExtraChatTagPayload(guid);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,13 +50,13 @@ public sealed class AccountRegistrationService : IDisposable
|
|||||||
var authApiUrl = _serverManager.CurrentApiUrl;
|
var authApiUrl = _serverManager.CurrentApiUrl;
|
||||||
|
|
||||||
// Override the API URL used for auth from remote config, if one is available
|
// Override the API URL used for auth from remote config, if one is available
|
||||||
if (authApiUrl.Equals(ApiController.LoporritServiceUri, StringComparison.Ordinal))
|
if (authApiUrl.Equals(ApiController.ClubPenguinServiceUri, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
var config = await _remoteConfig.GetConfigAsync<HubConnectionConfig>("mainServer").ConfigureAwait(false) ?? new();
|
var config = await _remoteConfig.GetConfigAsync<HubConnectionConfig>("mainServer").ConfigureAwait(false) ?? new();
|
||||||
if (!string.IsNullOrEmpty(config.ApiUrl))
|
if (!string.IsNullOrEmpty(config.ApiUrl))
|
||||||
authApiUrl = config.ApiUrl;
|
authApiUrl = config.ApiUrl;
|
||||||
else
|
else
|
||||||
authApiUrl = ApiController.LoporritServiceApiUri;
|
authApiUrl = ApiController.ClubPenguinServiceApiUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
var secretKey = GenerateSecretKey();
|
var secretKey = GenerateSecretKey();
|
||||||
|
|||||||
@@ -425,6 +425,18 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
private void PersistFileToStorage(string fileHash, string filePath, long? compressedSize = null)
|
private void PersistFileToStorage(string fileHash, string filePath, long? compressedSize = null)
|
||||||
{
|
{
|
||||||
|
var fi = new FileInfo(filePath);
|
||||||
|
Func<DateTime> RandomDayInThePast()
|
||||||
|
{
|
||||||
|
DateTime start = new(1995, 1, 1, 1, 1, 1, DateTimeKind.Local);
|
||||||
|
Random gen = new();
|
||||||
|
int range = (DateTime.Today - start).Days;
|
||||||
|
return () => start.AddDays(gen.Next(range));
|
||||||
|
}
|
||||||
|
|
||||||
|
fi.CreationTime = RandomDayInThePast().Invoke();
|
||||||
|
fi.LastAccessTime = DateTime.Today;
|
||||||
|
fi.LastWriteTime = RandomDayInThePast().Invoke();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var entry = _fileDbManager.CreateCacheEntry(filePath, fileHash);
|
var entry = _fileDbManager.CreateCacheEntry(filePath, fileHash);
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ namespace MareSynchronos.WebAPI;
|
|||||||
#pragma warning disable MA0040
|
#pragma warning disable MA0040
|
||||||
public sealed partial class ApiController : DisposableMediatorSubscriberBase, IMareHubClient
|
public sealed partial class ApiController : DisposableMediatorSubscriberBase, IMareHubClient
|
||||||
{
|
{
|
||||||
public const string LoporritServer = "Loporrit Main Server";
|
public const string ClubPenguinServer = "Club Penguin Sync Main Server";
|
||||||
public const string LoporritServiceUri = "wss://lop-sync.com";
|
public const string ClubPenguinServiceUri = "wss://clubpenguin.drgn.rocks";
|
||||||
public const string LoporritServiceApiUri = "wss://hub.lop-sync.com/";
|
public const string ClubPenguinServiceApiUri = "wss://clubpenguin.drgn.rocks/";
|
||||||
public const string LoporritServiceHubUri = "wss://hub.lop-sync.com/mare";
|
public const string ClubPenguinServiceHubUri = "wss://clubpenguin.drgn.rocks/mare";
|
||||||
|
|
||||||
private readonly DalamudUtilService _dalamudUtil;
|
private readonly DalamudUtilService _dalamudUtil;
|
||||||
private readonly HubFactory _hubFactory;
|
private readonly HubFactory _hubFactory;
|
||||||
@@ -192,9 +192,9 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
|||||||
if (_connectionDto.CurrentClientVersion > currentClientVer)
|
if (_connectionDto.CurrentClientVersion > currentClientVer)
|
||||||
{
|
{
|
||||||
Mediator.Publish(new NotificationMessage("Client incompatible",
|
Mediator.Publish(new NotificationMessage("Client incompatible",
|
||||||
$"Your client is outdated ({currentClientVer.Major}.{currentClientVer.Minor}.{currentClientVer.Build}), current is: " +
|
$"Your client is outdated ({currentClientVer.Major}.{currentClientVer.Minor}.{currentClientVer.Build}.{currentClientVer.Revision}), current is: " +
|
||||||
$"{_connectionDto.CurrentClientVersion.Major}.{_connectionDto.CurrentClientVersion.Minor}.{_connectionDto.CurrentClientVersion.Build}. " +
|
$"{_connectionDto.CurrentClientVersion.Major}.{_connectionDto.CurrentClientVersion.Minor}.{_connectionDto.CurrentClientVersion.Build}.{_connectionDto.CurrentClientVersion.Revision} " +
|
||||||
$"This client version is incompatible and will not be able to connect. Please update your Loporrit client.",
|
$"This client version is incompatible and will not be able to connect. Please update your Club Penguin Sync client.",
|
||||||
NotificationType.Error));
|
NotificationType.Error));
|
||||||
}
|
}
|
||||||
await StopConnection(ServerState.VersionMisMatch).ConfigureAwait(false);
|
await StopConnection(ServerState.VersionMisMatch).ConfigureAwait(false);
|
||||||
@@ -204,9 +204,9 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
|||||||
if (_connectionDto.CurrentClientVersion > currentClientVer)
|
if (_connectionDto.CurrentClientVersion > currentClientVer)
|
||||||
{
|
{
|
||||||
Mediator.Publish(new NotificationMessage("Client outdated",
|
Mediator.Publish(new NotificationMessage("Client outdated",
|
||||||
$"Your client is outdated ({currentClientVer.Major}.{currentClientVer.Minor}.{currentClientVer.Build}), current is: " +
|
$"Your client is outdated ({currentClientVer.Major}.{currentClientVer.Minor}.{currentClientVer.Build}.{currentClientVer.Revision}), current is: " +
|
||||||
$"{_connectionDto.CurrentClientVersion.Major}.{_connectionDto.CurrentClientVersion.Minor}.{_connectionDto.CurrentClientVersion.Build}. " +
|
$"{_connectionDto.CurrentClientVersion.Major}.{_connectionDto.CurrentClientVersion.Minor}.{_connectionDto.CurrentClientVersion.Build}.{_connectionDto.CurrentClientVersion.Revision} " +
|
||||||
$"Please keep your Loporrit client up-to-date.",
|
$"Please keep your Club Penguin Sync client up-to-date.",
|
||||||
NotificationType.Warning, TimeSpan.FromSeconds(15)));
|
NotificationType.Warning, TimeSpan.FromSeconds(15)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,14 +87,14 @@ public class HubFactory : MediatorSubscriberBase
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_serverConfigurationManager.CurrentApiUrl.Equals(ApiController.LoporritServiceUri, StringComparison.Ordinal))
|
if (_serverConfigurationManager.CurrentApiUrl.Equals(ApiController.ClubPenguinServiceUri, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
var mainServerConfig = await _remoteConfig.GetConfigAsync<HubConnectionConfig>("mainServer").ConfigureAwait(false) ?? new();
|
var mainServerConfig = await _remoteConfig.GetConfigAsync<HubConnectionConfig>("mainServer").ConfigureAwait(false) ?? new();
|
||||||
defaultConfig = mainServerConfig;
|
defaultConfig = mainServerConfig;
|
||||||
if (string.IsNullOrEmpty(mainServerConfig.ApiUrl))
|
if (string.IsNullOrEmpty(mainServerConfig.ApiUrl))
|
||||||
defaultConfig.ApiUrl = ApiController.LoporritServiceApiUri;
|
defaultConfig.ApiUrl = ApiController.ClubPenguinServiceApiUri;
|
||||||
if (string.IsNullOrEmpty(mainServerConfig.HubUrl))
|
if (string.IsNullOrEmpty(mainServerConfig.HubUrl))
|
||||||
defaultConfig.HubUrl = ApiController.LoporritServiceHubUri;
|
defaultConfig.HubUrl = ApiController.ClubPenguinServiceHubUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
string jsonResponse;
|
string jsonResponse;
|
||||||
|
|||||||
@@ -73,13 +73,13 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber
|
|||||||
var authApiUrl = _serverManager.CurrentApiUrl;
|
var authApiUrl = _serverManager.CurrentApiUrl;
|
||||||
|
|
||||||
// Override the API URL used for auth from remote config, if one is available
|
// Override the API URL used for auth from remote config, if one is available
|
||||||
if (authApiUrl.Equals(ApiController.LoporritServiceUri, StringComparison.Ordinal))
|
if (authApiUrl.Equals(ApiController.ClubPenguinServiceUri, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
var config = await _remoteConfig.GetConfigAsync<HubConnectionConfig>("mainServer").ConfigureAwait(false) ?? new();
|
var config = await _remoteConfig.GetConfigAsync<HubConnectionConfig>("mainServer").ConfigureAwait(false) ?? new();
|
||||||
if (!string.IsNullOrEmpty(config.ApiUrl))
|
if (!string.IsNullOrEmpty(config.ApiUrl))
|
||||||
authApiUrl = config.ApiUrl;
|
authApiUrl = config.ApiUrl;
|
||||||
else
|
else
|
||||||
authApiUrl = ApiController.LoporritServiceApiUri;
|
authApiUrl = ApiController.ClubPenguinServiceApiUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
Submodule Penumbra.Api updated: d8e0ad6ee2...dd14131793
Reference in New Issue
Block a user