Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
91d4cf8532 | |||
df7e6b2a51 | |||
b9ce34bf10 | |||
267fb2d25c | |||
291f005888
|
|||
2e1bc4840b | |||
8526f6c240 | |||
77d62b68b4 | |||
bc72660c7f | |||
8a27634d06 | |||
d7355c5eb9 | |||
46558bf4f9 | |||
e5dd13bc3e |
@@ -225,9 +225,15 @@ public sealed class IpcCallerPenumbra : DisposableMediatorSubscriberBase, IIpcCa
|
|||||||
|
|
||||||
return await _dalamudUtil.RunOnFrameworkThread(() =>
|
return await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||||
{
|
{
|
||||||
|
Guid collId;
|
||||||
var collName = "ElfSync_" + uid;
|
var collName = "ElfSync_" + uid;
|
||||||
var collId = _penumbraCreateNamedTemporaryCollection.Invoke(collName);
|
PenumbraApiEc penEC = _penumbraCreateNamedTemporaryCollection.Invoke(uid, collName, out collId);
|
||||||
logger.LogTrace("Creating Temp Collection {collName}, GUID: {collId}", collName, collId);
|
logger.LogTrace("Creating Temp Collection {collName}, GUID: {collId}", collName, collId);
|
||||||
|
if (penEC != PenumbraApiEc.Success)
|
||||||
|
{
|
||||||
|
logger.LogError("Failed to create temporary collection for {collName} with error code {penEC}. Please include this line in any error reports", collName, penEC);
|
||||||
|
return Guid.Empty;
|
||||||
|
}
|
||||||
return collId;
|
return collId;
|
||||||
|
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
|
@@ -7,9 +7,9 @@ public class PlayerPerformanceConfig : IMareConfiguration
|
|||||||
public int Version { get; set; } = 1;
|
public int Version { get; set; } = 1;
|
||||||
public bool AutoPausePlayersExceedingThresholds { get; set; } = true;
|
public bool AutoPausePlayersExceedingThresholds { get; set; } = true;
|
||||||
public bool NotifyAutoPauseDirectPairs { get; set; } = true;
|
public bool NotifyAutoPauseDirectPairs { get; set; } = true;
|
||||||
public bool NotifyAutoPauseGroupPairs { get; set; } = false;
|
public bool NotifyAutoPauseGroupPairs { get; set; } = true;
|
||||||
public int VRAMSizeAutoPauseThresholdMiB { get; set; } = 500;
|
public int VRAMSizeAutoPauseThresholdMiB { get; set; } = 500;
|
||||||
public int TrisAutoPauseThresholdThousands { get; set; } = 175;
|
public int TrisAutoPauseThresholdThousands { get; set; } = 400;
|
||||||
public bool IgnoreDirectPairs { get; set; } = true;
|
public bool IgnoreDirectPairs { get; set; } = true;
|
||||||
public TextureShrinkMode TextureShrinkMode { get; set; } = TextureShrinkMode.Default;
|
public TextureShrinkMode TextureShrinkMode { get; set; } = TextureShrinkMode.Default;
|
||||||
public bool TextureShrinkDeleteOriginal { get; set; } = false;
|
public bool TextureShrinkDeleteOriginal { get; set; } = false;
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
<Project Sdk="Dalamud.NET.Sdk/13.0.0">
|
<Project Sdk="Dalamud.NET.Sdk/13.0.0">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>Snowcloak</AssemblyName>
|
<AssemblyName>Snowcloak</AssemblyName>
|
||||||
<Version>0.1.6</Version>
|
<Version>0.1.8.1</Version>
|
||||||
<PackageProjectUrl>https://github.com/Eauldane/SnowcloakClient/</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/Eauldane/SnowcloakClient/</PackageProjectUrl>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="!Exists('.\Penumbra.Api\Penumbra.Api.csproj')">
|
<ItemGroup Condition="!Exists('.\Penumbra.Api\Penumbra.Api.csproj')">
|
||||||
<PackageReference Include="Penumbra.Api" Version="5.10.0" />
|
<PackageReference Include="Penumbra.Api" Version="5.12.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition="Exists('.\Glamourer.Api\Glamourer.Api.csproj')">
|
<ItemGroup Condition="Exists('.\Glamourer.Api\Glamourer.Api.csproj')">
|
||||||
|
@@ -86,8 +86,8 @@ public class Pair : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
Name = name,
|
Name = name,
|
||||||
OnClicked = action,
|
OnClicked = action,
|
||||||
PrefixColor = 559,
|
PrefixColor = 526,
|
||||||
PrefixChar = 'L'
|
PrefixChar = 'S'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -126,7 +126,6 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
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>();
|
||||||
@@ -210,7 +209,6 @@ 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>());
|
collection.AddHostedService(p => p.GetRequiredService<NoSnapService>());
|
||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
@@ -56,112 +56,9 @@ public sealed class RemoteConfigurationService
|
|||||||
|
|
||||||
private async Task DownloadConfig()
|
private async Task DownloadConfig()
|
||||||
{
|
{
|
||||||
string? jsonResponse = null;
|
// Removed Lop's remote config code. Function exists purely to keep things clean.
|
||||||
|
LoadConfig();
|
||||||
foreach (var remoteUrl in ConfigSources)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Fetching {url}", remoteUrl);
|
|
||||||
|
|
||||||
using var httpClient = new HttpClient(
|
|
||||||
new HttpClientHandler
|
|
||||||
{
|
|
||||||
AllowAutoRedirect = true,
|
|
||||||
MaxAutomaticRedirections = 5
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
httpClient.Timeout = TimeSpan.FromSeconds(6);
|
|
||||||
|
|
||||||
var ver = Assembly.GetExecutingAssembly().GetName().Version;
|
|
||||||
httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("MareSynchronos", ver!.Major + "." + ver!.Minor + "." + ver!.Build));
|
|
||||||
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, remoteUrl);
|
|
||||||
|
|
||||||
if (remoteUrl.Equals(_configService.Current.Origin, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(_configService.Current.ETag))
|
|
||||||
request.Headers.IfNoneMatch.Add(new EntityTagHeaderValue(_configService.Current.ETag));
|
|
||||||
|
|
||||||
if (_configService.Current.LastModified != null)
|
|
||||||
request.Headers.IfModifiedSince = _configService.Current.LastModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = await httpClient.SendAsync(request).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (response.StatusCode == HttpStatusCode.NotModified)
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Using cached remote configuration from {url}", remoteUrl);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
|
|
||||||
var contentType = response.Content.Headers.ContentType?.MediaType;
|
|
||||||
|
|
||||||
if (contentType == null || !contentType.Equals("application/json", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
_logger.LogWarning("HTTP request for remote config failed: wrong MIME type");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.LogInformation("Downloaded new configuration from {url}", remoteUrl);
|
|
||||||
|
|
||||||
_configService.Current.Origin = remoteUrl;
|
|
||||||
_configService.Current.ETag = response.Headers.ETag?.ToString() ?? string.Empty;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (response.Content.Headers.Contains("Last-Modified"))
|
|
||||||
{
|
|
||||||
var lastModified = response.Content.Headers.GetValues("Last-Modified").First();
|
|
||||||
_configService.Current.LastModified = DateTimeOffset.Parse(lastModified, System.Globalization.CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
_configService.Current.LastModified = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "HTTP request for remote config failed");
|
|
||||||
|
|
||||||
if (remoteUrl.Equals(_configService.Current.Origin, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
_configService.Current.ETag = string.Empty;
|
|
||||||
_configService.Current.LastModified = null;
|
|
||||||
_configService.Save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jsonResponse == null)
|
|
||||||
{
|
|
||||||
_logger.LogWarning("Could not download remote config");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var jsonDoc = JsonNode.Parse(jsonResponse) as JsonObject;
|
|
||||||
|
|
||||||
if (jsonDoc == null)
|
|
||||||
{
|
|
||||||
_logger.LogWarning("Downloaded remote config is not a JSON object");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadConfig(jsonDoc);
|
|
||||||
}
|
|
||||||
catch (JsonException ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Invalid JSON in remote config response");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool VerifySignature(string message, ulong ts, string signature, string pubKey)
|
private static bool VerifySignature(string message, ulong ts, string signature, string pubKey)
|
||||||
@@ -172,29 +69,12 @@ public sealed class RemoteConfigurationService
|
|||||||
return Ed25519.Verify(sig, msg, pub);
|
return Ed25519.Verify(sig, msg, pub);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadConfig(JsonObject jsonDoc)
|
private void LoadConfig()
|
||||||
{
|
{
|
||||||
ulong ts = 1755859494;
|
ulong ts = 1755859494;
|
||||||
|
|
||||||
//if (ts <= _configService.Current.Timestamp)
|
var configString = "{\"mainServer\":{\"api_url\":\"wss://hub.snowcloak-sync.com/\",\"hub_url\":\"wss://hub.snowcloak-sync.com/mare\"},\"noSnap\":{\"listOfPlugins\":[\"Snapper\",\"Snappy\",\"Meddle.Plugin\"]}}";
|
||||||
//{
|
|
||||||
// _logger.LogDebug("Remote configuration is not newer than cached config");
|
|
||||||
// return;
|
|
||||||
//}
|
|
||||||
|
|
||||||
var signatures = jsonDoc["sig"]!.AsObject();
|
|
||||||
var configString = "{\"mainServer\":{\"api_url\":\"wss://hub.snowcloak-sync.com/\",\"hub_url\":\"wss://hub.snowcloak-sync.com/mare\"},\"repoChange\":{\"current_repo\":\"https://hub.snowcloak-sync.com/repo.json\",\"valid_repos\":[\"https://hub.snowcloak-sync.com/repo.json\"]},\"noSnap\":{\"listOfPlugins\":[\"Snapper\",\"Snappy\",\"Meddle.Plugin\"]}}";
|
|
||||||
// var configString = jsonDoc["config"]!.GetValue<string>();
|
|
||||||
// bool verified = signatures.Any(sig =>
|
|
||||||
// ConfigPublicKeys.TryGetValue(sig.Key, out var pubKey) &&
|
|
||||||
// VerifySignature(configString, ts, sig.Value!.GetValue<string>(), pubKey));
|
|
||||||
|
|
||||||
//bool verified = true;
|
|
||||||
// if (!verified)
|
|
||||||
// {
|
|
||||||
// _logger.LogWarning("Could not verify signature for downloaded remote config");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
_configService.Current.Configuration = JsonNode.Parse(configString)!.AsObject();
|
_configService.Current.Configuration = JsonNode.Parse(configString)!.AsObject();
|
||||||
_configService.Current.Timestamp = ts;
|
_configService.Current.Timestamp = ts;
|
||||||
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -8,7 +8,7 @@
|
|||||||
"Tags": [
|
"Tags": [
|
||||||
"customization"
|
"customization"
|
||||||
],
|
],
|
||||||
"IconUrl": "https://i.imgur.com/cMKeOjm.jpeg",
|
"IconUrl": "https://raw.githubusercontent.com/Eauldane/SnowcloakClient/refs/heads/main/MareSynchronos/images/logo.png",
|
||||||
"RepoUrl": "https://github.com/Eauldane/SnowcloakClient",
|
"RepoUrl": "https://github.com/Eauldane/SnowcloakClient",
|
||||||
"CanUnloadAsync": true
|
"CanUnloadAsync": true
|
||||||
}
|
}
|
||||||
|
@@ -169,7 +169,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
|||||||
var dlProgressPercent = transferredBytes / (double)totalBytes;
|
var dlProgressPercent = transferredBytes / (double)totalBytes;
|
||||||
drawList.AddRectFilled(dlBarStart,
|
drawList.AddRectFilled(dlBarStart,
|
||||||
dlBarEnd with { X = dlBarStart.X + (float)(dlProgressPercent * dlBarWidth) },
|
dlBarEnd with { X = dlBarStart.X + (float)(dlProgressPercent * dlBarWidth) },
|
||||||
UiSharedService.Color(150, 150, 255, transparency), 1);
|
UiSharedService.Color(100, 100, 255, transparency), 1);
|
||||||
|
|
||||||
if (_configService.Current.TransferBarsShowText)
|
if (_configService.Current.TransferBarsShowText)
|
||||||
{
|
{
|
||||||
|
@@ -113,9 +113,9 @@
|
|||||||
},
|
},
|
||||||
"Penumbra.Api": {
|
"Penumbra.Api": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[5.10.0, )",
|
"requested": "[5.12.0, )",
|
||||||
"resolved": "5.10.0",
|
"resolved": "5.12.0",
|
||||||
"contentHash": "ZRIIXQCluNlLoI4I4gNYuYXqb7p48OR1P2/LcL27owToR/A/IG0mH8Ks2HguB/Kjq4RpCinaVkyufpGxGspbTA=="
|
"contentHash": "XGWviAZgokj2djpH50FWgM24jOTpKUuDHvd0HwrzBRY6BEMmpb3HfGIl8+BDE/DqbpH63u6aO2TvzUV6BmXT5w=="
|
||||||
},
|
},
|
||||||
"SonarAnalyzer.CSharp": {
|
"SonarAnalyzer.CSharp": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
|
Reference in New Issue
Block a user