FINALY !!!

This commit is contained in:
2025-09-05 21:34:17 +02:00
parent eeab8354b6
commit 46f2443824
28 changed files with 150 additions and 430 deletions

BIN
.DS_Store vendored

Binary file not shown.

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@
*.user *.user
*.userosscache *.userosscache
*.sln.docstates *.sln.docstates
.DS_Store
# User-specific files (MonoDevelop/Xamarin Studio) # User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs *.userprefs

Binary file not shown.

View File

@@ -236,7 +236,6 @@ public sealed class FileCacheManager : IHostedService
foreach (var entry in cleanedPaths) foreach (var entry in cleanedPaths)
{ {
//_logger.LogDebug("Checking {path}", entry.Value);
if (dict.TryGetValue(entry.Value, out var entity)) if (dict.TryGetValue(entry.Value, out var entity))
{ {
@@ -366,7 +365,6 @@ public sealed class FileCacheManager : IHostedService
if (!entries.Exists(u => string.Equals(u.PrefixedFilePath, fileCache.PrefixedFilePath, StringComparison.OrdinalIgnoreCase))) if (!entries.Exists(u => string.Equals(u.PrefixedFilePath, fileCache.PrefixedFilePath, StringComparison.OrdinalIgnoreCase)))
{ {
//_logger.LogTrace("Adding to DB: {hash} => {path}", fileCache.Hash, fileCache.PrefixedFilePath);
entries.Add(fileCache); entries.Add(fileCache);
} }
} }
@@ -389,7 +387,6 @@ public sealed class FileCacheManager : IHostedService
private FileCacheEntity? GetValidatedFileCache(FileCacheEntity fileCache) private FileCacheEntity? GetValidatedFileCache(FileCacheEntity fileCache)
{ {
var resultingFileCache = ReplacePathPrefixes(fileCache); var resultingFileCache = ReplacePathPrefixes(fileCache);
//_logger.LogTrace("Validating {path}", fileCache.PrefixedFilePath);
resultingFileCache = Validate(resultingFileCache); resultingFileCache = Validate(resultingFileCache);
return resultingFileCache; return resultingFileCache;
} }

View File

@@ -95,8 +95,6 @@ public sealed class IpcCallerBrio : IIpcCaller
if (gameObject == null) return default; if (gameObject == null) return default;
var data = await _dalamudUtilService.RunOnFrameworkThread(() => _brioGetModelTransform.InvokeFunc(gameObject)).ConfigureAwait(false); var data = await _dalamudUtilService.RunOnFrameworkThread(() => _brioGetModelTransform.InvokeFunc(gameObject)).ConfigureAwait(false);
if (data.Item1 == null || data.Item2 == null || data.Item3 == null) return default; if (data.Item1 == null || data.Item2 == null || data.Item3 == null) return default;
//_logger.LogDebug("Getting Transform from Actor {actor}", gameObject.Name.TextValue);
return new WorldData() return new WorldData()
{ {
PositionX = data.Item1.Value.X, PositionX = data.Item1.Value.X,

View File

@@ -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
{ {

View File

@@ -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

View File

@@ -8,6 +8,6 @@ public static class ConfigurationExtensions
{ {
return configuration.AcceptedAgreement && configuration.InitialScanComplete return configuration.AcceptedAgreement && configuration.InitialScanComplete
&& !string.IsNullOrEmpty(configuration.CacheFolder) && !string.IsNullOrEmpty(configuration.CacheFolder)
&& Directory.Exists(configuration.CacheFolder) && configuration.AcceptedTOSVersion == configuration.ExpectedTOSVersion; && Directory.Exists(configuration.CacheFolder);
} }
} }

View File

@@ -1,10 +1,10 @@
<?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.0.0">
<PropertyGroup> <PropertyGroup>
<AssemblyName>UmbraSync</AssemblyName> <AssemblyName>UmbraSync</AssemblyName>
<Version>0.1.0.0</Version> <RootNamespace>UmbraSync</RootNamespace>
<PackageProjectUrl>https://git.umbra-sync.net/SirConstance/UmbraClient</PackageProjectUrl> <Version>0.1.1.0</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Remove="PlayerData\Export\**" /> <Compile Remove="PlayerData\Export\**" />

View File

@@ -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);
} }
} }

View File

@@ -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);
@@ -319,24 +316,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
}); });
} }
private void RegisterGposeClones()
{
var name = PlayerName;
if (name == null)
return;
_ = _dalamudUtil.RunOnFrameworkThread(() =>
{
foreach (var actor in _dalamudUtil.GetGposeCharactersFromObjectTable())
{
if (actor == null) continue;
var gposeName = actor.Name.TextValue;
if (!name.Equals(gposeName, StringComparison.Ordinal))
continue;
_noSnapService.AddGposer(actor.ObjectIndex);
}
});
}
private async Task UndoApplicationAsync(Guid applicationId = default) private async Task UndoApplicationAsync(Guid applicationId = default)
{ {
Logger.LogDebug($"Undoing application of {Pair.UserPair}"); Logger.LogDebug($"Undoing application of {Pair.UserPair}");
@@ -353,7 +332,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
{ {
await _ipcManager.Penumbra.RemoveTemporaryCollectionAsync(Logger, applicationId, _penumbraCollection).ConfigureAwait(false); await _ipcManager.Penumbra.RemoveTemporaryCollectionAsync(Logger, applicationId, _penumbraCollection).ConfigureAwait(false);
_penumbraCollection = Guid.Empty; _penumbraCollection = Guid.Empty;
RegisterGposeClones();
} }
if (_dalamudUtil is { IsZoning: false, IsInCutscene: false } && !string.IsNullOrEmpty(name)) if (_dalamudUtil is { IsZoning: false, IsInCutscene: false } && !string.IsNullOrEmpty(name))
@@ -385,10 +363,6 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
} }
} }
} }
else if (_dalamudUtil.IsInCutscene && !string.IsNullOrEmpty(name))
{
_noSnapService.AddGposerNamed(name);
}
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -171,11 +171,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);
} }

View File

@@ -39,8 +39,6 @@ public sealed class Plugin : IDalamudPlugin
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 UmbraSync namespace to avoid confusion in /xlstats
public void OnFrameworkUpdate(IFramework framework) public void OnFrameworkUpdate(IFramework framework)
{ {
RealOnFrameworkUpdate?.Invoke(framework); RealOnFrameworkUpdate?.Invoke(framework);
@@ -142,7 +140,6 @@ public sealed class Plugin : IDalamudPlugin
collection.AddSingleton<IpcCallerMare>(); collection.AddSingleton<IpcCallerMare>();
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));
@@ -168,7 +165,6 @@ public sealed class Plugin : IDalamudPlugin
collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<RemoteConfigCacheService>()); collection.AddSingleton<IConfigService<IMareConfiguration>>(s => s.GetRequiredService<RemoteConfigCacheService>());
collection.AddSingleton<ConfigurationMigrator>(); collection.AddSingleton<ConfigurationMigrator>();
collection.AddSingleton<ConfigurationSaveService>(); collection.AddSingleton<ConfigurationSaveService>();
collection.AddSingleton<RemoteConfigurationService>();
collection.AddSingleton<HubFactory>(); collection.AddSingleton<HubFactory>();
@@ -209,7 +205,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<NoSnapService>());
}) })
.Build(); .Build();

View File

@@ -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);
}
} }

View File

@@ -72,7 +72,7 @@ public class ChatService : DisposableMediatorSubscriberBase
{ {
var chatMsg = message.ChatMsg; var chatMsg = message.ChatMsg;
var prefix = new SeStringBuilder(); var prefix = new SeStringBuilder();
prefix.AddText("[BnnuyChat] "); prefix.AddText("[UmbraChat] ");
_chatGui.Print(new XivChatEntry{ _chatGui.Print(new XivChatEntry{
MessageBytes = [..prefix.Build().Encode(), ..message.ChatMsg.PayloadContent], MessageBytes = [..prefix.Build().Encode(), ..message.ChatMsg.PayloadContent],
Name = chatMsg.SenderName, Name = chatMsg.SenderName,
@@ -207,7 +207,7 @@ public class ChatService : DisposableMediatorSubscriberBase
} }
} }
_chatGui.PrintError($"[Umbra] Syncshell number #{shellNumber} not found"); _chatGui.PrintError($"[UmbraSync] 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($"[Umbra] Syncshell number #{shellNumber} not found"); _chatGui.PrintError($"[UmbraSync] Syncshell number #{shellNumber} not found");
} }
} }

View File

@@ -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 = "/snowcloak"; private const string _commandName2 = "/usync";
private const string _ssCommandPrefix = "/ss"; private const string _ssCommandPrefix = "/ss";

View File

@@ -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;
}
}
}
}

View File

@@ -41,19 +41,19 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
private void PrintErrorChat(string? message) private void PrintErrorChat(string? message)
{ {
SeStringBuilder se = new SeStringBuilder().AddText("[Umbra] Error: " + message); SeStringBuilder se = new SeStringBuilder().AddText("[UmbraSync] 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("[Umbra] Info: ").AddItalics(message ?? string.Empty); SeStringBuilder se = new SeStringBuilder().AddText("[UmbraSync] 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("[Umbra] ").AddUiForeground("Warning: " + (message ?? string.Empty), 31).AddUiForegroundOff(); SeStringBuilder se = new SeStringBuilder().AddText("[UmbraSync] ").AddUiForeground("Warning: " + (message ?? string.Empty), 31).AddUiForegroundOff();
_chatGui.Print(se.BuiltString); _chatGui.Print(se.BuiltString);
} }

View File

@@ -1,21 +0,0 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace MareSynchronos.Services
{
/// <summary>
/// Stub minimal : renvoie toujours null (pas de conf distante).
/// </summary>
public class RemoteConfigurationService
{
private readonly ILogger<RemoteConfigurationService> _logger;
public RemoteConfigurationService(ILogger<RemoteConfigurationService> logger)
{
_logger = logger;
}
public Task<T?> GetConfigAsync<T>(string key) where T : class
=> Task.FromResult<T?>(null);
}
}

View File

@@ -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, "###UmbraSyncMainUI", performanceCollectorService) : base(logger, mediator, "###UmbraSyncSyncMainUI", 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 = $"Umbra Sync {dev} ({ver.Major}.{ver.Minor}.{ver.Build})###UmbraSyncMainUIDev"; WindowName = $"UmbraSync {dev} ({ver.Major}.{ver.Minor}.{ver.Build})###UmbraSyncSyncMainUIDev";
Toggle(); Toggle();
#else #else
var ver = Assembly.GetExecutingAssembly().GetName().Version!; var ver = Assembly.GetExecutingAssembly().GetName().Version!;
WindowName = "Umbra Sync " + ver.Major + "." + ver.Minor + "." + ver.Build + "###UmbraSyncMainUI"; WindowName = "UmbraSync " + ver.Major + "." + ver.Minor + "." + ver.Build + "###UmbraSyncSyncMainUI";
#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,10 +104,7 @@ public class CompactUi : WindowMediatorSubscriberBase
protected override void DrawInternal() protected override void DrawInternal()
{ {
if (_serverManager.CurrentApiUrl.Equals(ApiController.UmbraServiceUri, StringComparison.Ordinal)) UiSharedService.AccentColor = new Vector4(0.2f, 0.6f, 1f, 1f); // custom blue
UiSharedService.AccentColor = new(0.4275f, 0.6863f, 1f, 1f);
else
UiSharedService.AccentColor = new Vector4(0.6f, 0.4f, 0.8f, 1f);
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);
WindowContentWidth = UiSharedService.GetWindowContentRegionWidth(); WindowContentWidth = UiSharedService.GetWindowContentRegionWidth();
if (!_apiController.IsCurrentVersion) if (!_apiController.IsCurrentVersion)
@@ -121,8 +118,8 @@ public class CompactUi : WindowMediatorSubscriberBase
ImGui.AlignTextToFramePadding(); ImGui.AlignTextToFramePadding();
ImGui.TextColored(ImGuiColors.DalamudRed, unsupported); ImGui.TextColored(ImGuiColors.DalamudRed, unsupported);
} }
UiSharedService.ColorTextWrapped($"Your Umbra installation is out of date, the current version is {ver.Major}.{ver.Minor}.{ver.Build}. " + UiSharedService.ColorTextWrapped($"Your UmbraSync installation is out of date, the current version is {ver.Major}.{ver.Minor}.{ver.Build}. " +
$"It is highly recommended to keep Umbra up to date. Open /xlplugins and update the plugin.", ImGuiColors.DalamudRed); $"It is highly recommended to keep UmbraSync up to date. Open /xlplugins and update the plugin.", ImGuiColors.DalamudRed);
} }
using (ImRaii.PushId("header")) DrawUIDHeader(); using (ImRaii.PushId("header")) DrawUIDHeader();
@@ -387,7 +384,7 @@ public class CompactUi : WindowMediatorSubscriberBase
{ {
ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth()) / 2 - (userSize.X + textSize.X) / 2 - ImGui.GetStyle().ItemSpacing.X / 2); ImGui.SetCursorPosX((ImGui.GetWindowContentRegionMin().X + UiSharedService.GetWindowContentRegionWidth()) / 2 - (userSize.X + textSize.X) / 2 - ImGui.GetStyle().ItemSpacing.X / 2);
if (!printShard) ImGui.AlignTextToFramePadding(); if (!printShard) ImGui.AlignTextToFramePadding();
ImGui.TextColored(new Vector4(0.6f, 0.4f, 0.8f, 1f), userCount); ImGui.TextColored(ImGuiColors.ParsedBlue, userCount);
ImGui.SameLine(); ImGui.SameLine();
if (!printShard) ImGui.AlignTextToFramePadding(); if (!printShard) ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted("Users Online"); ImGui.TextUnformatted("Users Online");
@@ -410,7 +407,7 @@ public class CompactUi : WindowMediatorSubscriberBase
{ {
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ((userSize.Y + textSize.Y) / 2 + shardTextSize.Y) / 2 - ImGui.GetStyle().ItemSpacing.Y + buttonSize.Y / 2); ImGui.SetCursorPosY(ImGui.GetCursorPosY() - ((userSize.Y + textSize.Y) / 2 + shardTextSize.Y) / 2 - ImGui.GetStyle().ItemSpacing.Y + buttonSize.Y / 2);
} }
var color = !_serverManager.CurrentServer!.FullPause ? new Vector4(0.6f, 0.4f, 0.8f, 1f) : ImGuiColors.DalamudRed; var color = UiSharedService.GetBoolColor(!_serverManager.CurrentServer!.FullPause);
var connectedIcon = !_serverManager.CurrentServer.FullPause ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink; var connectedIcon = !_serverManager.CurrentServer.FullPause ? FontAwesomeIcon.Link : FontAwesomeIcon.Unlink;
if (_apiController.ServerState is ServerState.Connected) if (_apiController.ServerState is ServerState.Connected)
@@ -525,7 +522,7 @@ public class CompactUi : WindowMediatorSubscriberBase
{ {
Mediator.Publish(new OpenSettingsUiMessage()); Mediator.Publish(new OpenSettingsUiMessage());
} }
UiSharedService.AttachToolTip("Open the Umbra Settings"); UiSharedService.AttachToolTip("Open the UmbraSync Settings");
ImGui.SameLine(); //Important to draw the uidText consistently ImGui.SameLine(); //Important to draw the uidText consistently
ImGui.SetCursorPos(originalPos); ImGui.SetCursorPos(originalPos);
@@ -595,7 +592,7 @@ public class CompactUi : WindowMediatorSubscriberBase
{ {
ServerState.Connecting => ImGuiColors.DalamudYellow, ServerState.Connecting => ImGuiColors.DalamudYellow,
ServerState.Reconnecting => ImGuiColors.DalamudRed, ServerState.Reconnecting => ImGuiColors.DalamudRed,
ServerState.Connected => UiSharedService.AccentColor, ServerState.Connected => new Vector4(0.2f, 0.6f, 1f, 1f), // custom blue
ServerState.Disconnected => ImGuiColors.DalamudYellow, ServerState.Disconnected => ImGuiColors.DalamudYellow,
ServerState.Disconnecting => ImGuiColors.DalamudYellow, ServerState.Disconnecting => ImGuiColors.DalamudYellow,
ServerState.Unauthorized => ImGuiColors.DalamudRed, ServerState.Unauthorized => ImGuiColors.DalamudRed,

View File

@@ -163,13 +163,13 @@ public class DownloadUi : WindowMediatorSubscriberBase
UiSharedService.Color(0, 0, 0, transparency), 1); UiSharedService.Color(0, 0, 0, transparency), 1);
drawList.AddRectFilled(dlBarStart with { X = dlBarStart.X - dlBarBorder, Y = dlBarStart.Y - dlBarBorder }, drawList.AddRectFilled(dlBarStart with { X = dlBarStart.X - dlBarBorder, Y = dlBarStart.Y - dlBarBorder },
dlBarEnd with { X = dlBarEnd.X + dlBarBorder, Y = dlBarEnd.Y + dlBarBorder }, dlBarEnd with { X = dlBarEnd.X + dlBarBorder, Y = dlBarEnd.Y + dlBarBorder },
UiSharedService.Color(200, 170, 230, transparency), 1); UiSharedService.Color(220, 220, 255, transparency), 1);
drawList.AddRectFilled(dlBarStart, dlBarEnd, drawList.AddRectFilled(dlBarStart, dlBarEnd,
UiSharedService.Color(0, 0, 0, transparency), 1); UiSharedService.Color(0, 0, 0, transparency), 1);
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(153, 102, 204, transparency), 1); UiSharedService.Color(100, 100, 255, transparency), 1);
if (_configService.Current.TransferBarsShowText) if (_configService.Current.TransferBarsShowText)
{ {
@@ -198,7 +198,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
var drawList = ImGui.GetBackgroundDrawList(); var drawList = ImGui.GetBackgroundDrawList();
UiSharedService.DrawOutlinedFont(drawList, uploadText, UiSharedService.DrawOutlinedFont(drawList, uploadText,
screenPos with { X = screenPos.X - textSize.X / 2f - 1, Y = screenPos.Y - textSize.Y / 2f - 1 }, screenPos with { X = screenPos.X - textSize.X / 2f - 1, Y = screenPos.Y - textSize.Y / 2f - 1 },
UiSharedService.Color(153, 102, 204, transparency), UiSharedService.Color(255, 255, 0, transparency),
UiSharedService.Color(0, 0, 0, transparency), 2); UiSharedService.Color(0, 0, 0, transparency), 2);
} }
catch catch

View File

@@ -106,9 +106,8 @@ public partial class IntroUi : WindowMediatorSubscriberBase
{ {
if (_uiShared.IsInGpose) return; if (_uiShared.IsInGpose) return;
if ((!_configService.Current.AcceptedAgreement || _configService.Current.AcceptedTOSVersion != _configService.Current.ExpectedTOSVersion) && !_readFirstPage) if (!_configService.Current.AcceptedAgreement && !_readFirstPage)
{ {
// TODO: The UI bugs hard if this page *isn't* shown before the new TOS. There's probably a way around it.
_uiShared.BigText("Welcome to Umbra"); _uiShared.BigText("Welcome to Umbra");
ImGui.Separator(); ImGui.Separator();
UiSharedService.TextWrapped("Umbra is a plugin that will replicate your full current character state including all Penumbra mods to other paired users. " + UiSharedService.TextWrapped("Umbra is a plugin that will replicate your full current character state including all Penumbra mods to other paired users. " +
@@ -126,7 +125,7 @@ public partial class IntroUi : WindowMediatorSubscriberBase
#if !DEBUG #if !DEBUG
_timeoutTask = Task.Run(async () => _timeoutTask = Task.Run(async () =>
{ {
for (int i = 45; i > 0; i--) for (int i = 10; i > 0; i--)
{ {
_timeoutLabel = $"'I agree' button will be available in {i}s"; _timeoutLabel = $"'I agree' button will be available in {i}s";
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false); await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
@@ -137,7 +136,7 @@ public partial class IntroUi : WindowMediatorSubscriberBase
#endif #endif
} }
} }
else if ((!_configService.Current.AcceptedAgreement || _configService.Current.AcceptedTOSVersion != _configService.Current.ExpectedTOSVersion) && _readFirstPage) else if (!_configService.Current.AcceptedAgreement && _readFirstPage)
{ {
using (_uiShared.UidFont.Push()) using (_uiShared.UidFont.Push())
{ {
@@ -152,7 +151,6 @@ public partial class IntroUi : WindowMediatorSubscriberBase
UiSharedService.ColorText(readThis, ImGuiColors.DalamudRed); UiSharedService.ColorText(readThis, ImGuiColors.DalamudRed);
ImGui.SetWindowFontScale(1.0f); ImGui.SetWindowFontScale(1.0f);
ImGui.Separator(); ImGui.Separator();
UiSharedService.TextWrapped(""" UiSharedService.TextWrapped("""
To use Umbra, you must be over the age of 18, or 21 in some jurisdictions. To use Umbra, you must be over the age of 18, or 21 in some jurisdictions.
"""); """);
@@ -172,7 +170,10 @@ public partial class IntroUi : WindowMediatorSubscriberBase
Mod files that are saved on the service will remain on the service as long as there are requests for the files from clients. After a period of not being used, the mod files will be automatically deleted. Mod files that are saved on the service will remain on the service as long as there are requests for the files from clients. After a period of not being used, the mod files will be automatically deleted.
"""); """);
UiSharedService.TextWrapped(""" UiSharedService.TextWrapped("""
Umbra is operated from servers located in the European Union and Switzerland. You agree not to upload any content to the service that violates EU and CH law; and more specifically, German law for EU. Accounts that are inactive for ninety (90) days will be deleted for privacy reasons.
""");
UiSharedService.TextWrapped("""
Umbra is operated from servers located in the European Union. You agree not to upload any content to the service that violates EU law; and more specifically, German law.
"""); """);
UiSharedService.TextWrapped(""" UiSharedService.TextWrapped("""
You may delete your account at any time from within the Settings panel of the plugin. Any mods unique to you will then be removed from the server within 14 days. You may delete your account at any time from within the Settings panel of the plugin. Any mods unique to you will then be removed from the server within 14 days.
@@ -187,7 +188,6 @@ public partial class IntroUi : WindowMediatorSubscriberBase
if (ImGui.Button("I agree##toSetup")) if (ImGui.Button("I agree##toSetup"))
{ {
_configService.Current.AcceptedAgreement = true; _configService.Current.AcceptedAgreement = true;
_configService.Current.AcceptedTOSVersion = _configService.Current.ExpectedTOSVersion;
_configService.Save(); _configService.Save();
} }
} }
@@ -196,7 +196,7 @@ public partial class IntroUi : WindowMediatorSubscriberBase
UiSharedService.TextWrapped(_timeoutLabel); UiSharedService.TextWrapped(_timeoutLabel);
} }
} }
else if ((!_configService.Current.AcceptedAgreement || _configService.Current.AcceptedTOSVersion != _configService.Current.ExpectedTOSVersion) else if (_configService.Current.AcceptedAgreement
&& (string.IsNullOrEmpty(_configService.Current.CacheFolder) && (string.IsNullOrEmpty(_configService.Current.CacheFolder)
|| !_configService.Current.InitialScanComplete || !_configService.Current.InitialScanComplete
|| !Directory.Exists(_configService.Current.CacheFolder))) || !Directory.Exists(_configService.Current.CacheFolder)))

View File

@@ -851,16 +851,6 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
ImGui.TextColored(ImGuiColors.DalamudRed, "You need to install both Penumbra and Glamourer and keep them up to date to use Umbra."); ImGui.TextColored(ImGuiColors.DalamudRed, "You need to install both Penumbra and Glamourer and keep them up to date to use Umbra.");
return false; 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 true; return true;
} }

View File

@@ -17,18 +17,16 @@ public sealed class AccountRegistrationService : IDisposable
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
private readonly ILogger<AccountRegistrationService> _logger; private readonly ILogger<AccountRegistrationService> _logger;
private readonly ServerConfigurationManager _serverManager; private readonly ServerConfigurationManager _serverManager;
private readonly RemoteConfigurationService _remoteConfig;
private string GenerateSecretKey() private string GenerateSecretKey()
{ {
return Convert.ToHexString(SHA256.HashData(RandomNumberGenerator.GetBytes(64))); return Convert.ToHexString(SHA256.HashData(RandomNumberGenerator.GetBytes(64)));
} }
public AccountRegistrationService(ILogger<AccountRegistrationService> logger, ServerConfigurationManager serverManager, RemoteConfigurationService remoteConfig) public AccountRegistrationService(ILogger<AccountRegistrationService> logger, ServerConfigurationManager serverManager)
{ {
_logger = logger; _logger = logger;
_serverManager = serverManager; _serverManager = serverManager;
_remoteConfig = remoteConfig;
_httpClient = new( _httpClient = new(
new HttpClientHandler new HttpClientHandler
{ {
@@ -47,22 +45,10 @@ public sealed class AccountRegistrationService : IDisposable
public async Task<RegisterReplyDto> RegisterAccount(CancellationToken token) public async Task<RegisterReplyDto> RegisterAccount(CancellationToken token)
{ {
var authApiUrl = _serverManager.CurrentApiUrl;
// Override the API URL used for auth from remote config, if one is available
if (authApiUrl.Equals(ApiController.UmbraServiceUri, StringComparison.Ordinal))
{
var config = await _remoteConfig.GetConfigAsync<HubConnectionConfig>("mainServer").ConfigureAwait(false) ?? new();
if (!string.IsNullOrEmpty(config.ApiUrl))
authApiUrl = config.ApiUrl;
else
authApiUrl = ApiController.UmbraServiceApiUri;
}
var secretKey = GenerateSecretKey(); var secretKey = GenerateSecretKey();
var hashedSecretKey = secretKey.GetHash256(); var hashedSecretKey = secretKey.GetHash256();
Uri postUri = MareAuth.AuthRegisterV2FullPath(new Uri(authApiUrl Uri postUri = MareAuth.AuthRegisterV2FullPath(new Uri(_serverManager.CurrentApiUrl
.Replace("wss://", "https://", StringComparison.OrdinalIgnoreCase) .Replace("wss://", "https://", StringComparison.OrdinalIgnoreCase)
.Replace("ws://", "http://", StringComparison.OrdinalIgnoreCase))); .Replace("ws://", "http://", StringComparison.OrdinalIgnoreCase)));

View File

@@ -21,8 +21,8 @@ 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 UmbraServer = "Umbra Main Server (BETA)"; public const string UmbraServer = "UmbraSync Main Server (BETA)";
public const string UmbraServiceUri = "wss://umbra-sync.net"; public const string UmbraServiceUri = "wss://umbra-sync.net/";
public const string UmbraServiceApiUri = "wss://umbra-sync.net/"; public const string UmbraServiceApiUri = "wss://umbra-sync.net/";
public const string UmbraServiceHubUri = "wss://umbra-sync.net/mare"; public const string UmbraServiceHubUri = "wss://umbra-sync.net/mare";

View File

@@ -9,6 +9,9 @@ using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.SignalR.Client; using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text.Json;
namespace MareSynchronos.WebAPI.SignalR; namespace MareSynchronos.WebAPI.SignalR;
@@ -16,7 +19,6 @@ public class HubFactory : MediatorSubscriberBase
{ {
private readonly ILoggerProvider _loggingProvider; private readonly ILoggerProvider _loggingProvider;
private readonly ServerConfigurationManager _serverConfigurationManager; private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly RemoteConfigurationService _remoteConfig;
private readonly TokenProvider _tokenProvider; private readonly TokenProvider _tokenProvider;
private HubConnection? _instance; private HubConnection? _instance;
private string _cachedConfigFor = string.Empty; private string _cachedConfigFor = string.Empty;
@@ -24,11 +26,10 @@ public class HubFactory : MediatorSubscriberBase
private bool _isDisposed = false; private bool _isDisposed = false;
public HubFactory(ILogger<HubFactory> logger, MareMediator mediator, public HubFactory(ILogger<HubFactory> logger, MareMediator mediator,
ServerConfigurationManager serverConfigurationManager, RemoteConfigurationService remoteConfig, ServerConfigurationManager serverConfigurationManager,
TokenProvider tokenProvider, ILoggerProvider pluginLog) : base(logger, mediator) TokenProvider tokenProvider, ILoggerProvider pluginLog) : base(logger, mediator)
{ {
_serverConfigurationManager = serverConfigurationManager; _serverConfigurationManager = serverConfigurationManager;
_remoteConfig = remoteConfig;
_tokenProvider = tokenProvider; _tokenProvider = tokenProvider;
_loggingProvider = pluginLog; _loggingProvider = pluginLog;
} }
@@ -65,28 +66,98 @@ public class HubFactory : MediatorSubscriberBase
private async Task<HubConnectionConfig> ResolveHubConfig() private async Task<HubConnectionConfig> ResolveHubConfig()
{ {
var stapledWellKnown = _tokenProvider.GetStapledWellKnown(_serverConfigurationManager.CurrentApiUrl);
var apiUrl = new Uri(_serverConfigurationManager.CurrentApiUrl);
HubConnectionConfig defaultConfig;
if (_cachedConfig != null && _serverConfigurationManager.CurrentApiUrl.Equals(_cachedConfigFor, StringComparison.Ordinal)) if (_cachedConfig != null && _serverConfigurationManager.CurrentApiUrl.Equals(_cachedConfigFor, StringComparison.Ordinal))
{ {
return _cachedConfig; defaultConfig = _cachedConfig;
} }
var defaultConfig = new HubConnectionConfig else
{
defaultConfig = new HubConnectionConfig
{ {
HubUrl = _serverConfigurationManager.CurrentApiUrl.TrimEnd('/') + IMareHub.Path, HubUrl = _serverConfigurationManager.CurrentApiUrl.TrimEnd('/') + IMareHub.Path,
Transports = [] Transports = []
}; };
if (_serverConfigurationManager.CurrentApiUrl.Equals(ApiController.UmbraServiceUri, StringComparison.Ordinal))
{
var mainServerConfig = await _remoteConfig.GetConfigAsync<HubConnectionConfig>("mainServer").ConfigureAwait(false) ?? new();
if (string.IsNullOrEmpty(mainServerConfig.ApiUrl))
mainServerConfig.ApiUrl = ApiController.UmbraServiceApiUri;
if (string.IsNullOrEmpty(mainServerConfig.HubUrl))
mainServerConfig.HubUrl = ApiController.UmbraServiceHubUri;
mainServerConfig.Transports ??= defaultConfig.Transports ?? [];
return mainServerConfig;
} }
string jsonResponse;
if (stapledWellKnown != null)
{
jsonResponse = stapledWellKnown;
Logger.LogTrace("Using stapled hub config for {url}", _serverConfigurationManager.CurrentApiUrl);
}
else
{
try
{
var httpScheme = apiUrl.Scheme.ToLowerInvariant() switch
{
"ws" => "http",
"wss" => "https",
_ => apiUrl.Scheme
};
var wellKnownUrl = $"{httpScheme}://{apiUrl.Host}/.well-known/Umbra/client";
Logger.LogTrace("Fetching hub config for {uri} via {wk}", _serverConfigurationManager.CurrentApiUrl, wellKnownUrl);
using var httpClient = new HttpClient(
new HttpClientHandler
{
AllowAutoRedirect = true,
MaxAutomaticRedirections = 5
}
);
var ver = Assembly.GetExecutingAssembly().GetName().Version;
httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("MareSynchronos", ver!.Major + "." + ver!.Minor + "." + ver!.Build));
var response = await httpClient.GetAsync(wellKnownUrl).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
return defaultConfig; return defaultConfig;
var contentType = response.Content.Headers.ContentType?.MediaType;
if (contentType == null || !contentType.Equals("application/json", StringComparison.Ordinal))
return defaultConfig;
jsonResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
catch (HttpRequestException ex)
{
Logger.LogWarning(ex, "HTTP request failed for .well-known");
return defaultConfig;
}
}
try
{
var config = JsonSerializer.Deserialize<HubConnectionConfig>(jsonResponse);
if (config == null)
return defaultConfig;
if (string.IsNullOrEmpty(config.ApiUrl))
config.ApiUrl = defaultConfig.ApiUrl;
if (string.IsNullOrEmpty(config.HubUrl))
config.HubUrl = defaultConfig.HubUrl;
config.Transports ??= defaultConfig.Transports ?? [];
return config;
}
catch (JsonException ex)
{
Logger.LogWarning(ex, "Invalid JSON in .well-known response");
return defaultConfig;
}
} }
private HubConnection BuildHubConnection(HubConnectionConfig hubConfig, CancellationToken ct) private HubConnection BuildHubConnection(HubConnectionConfig hubConfig, CancellationToken ct)
@@ -106,11 +177,13 @@ public class HubFactory : MediatorSubscriberBase
var resolver = CompositeResolver.Create(StandardResolverAllowPrivate.Instance, var resolver = CompositeResolver.Create(StandardResolverAllowPrivate.Instance,
BuiltinResolver.Instance, BuiltinResolver.Instance,
AttributeFormatterResolver.Instance, AttributeFormatterResolver.Instance,
// replace enum resolver
DynamicEnumAsStringResolver.Instance, DynamicEnumAsStringResolver.Instance,
DynamicGenericResolver.Instance, DynamicGenericResolver.Instance,
DynamicUnionResolver.Instance, DynamicUnionResolver.Instance,
DynamicObjectResolver.Instance, DynamicObjectResolver.Instance,
PrimitiveObjectResolver.Instance, PrimitiveObjectResolver.Instance,
// final fallback(last priority)
StandardResolver.Instance); StandardResolver.Instance);
opt.SerializerOptions = opt.SerializerOptions =

View File

@@ -20,16 +20,14 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber
private readonly HttpClient _httpClient; private readonly HttpClient _httpClient;
private readonly ILogger<TokenProvider> _logger; private readonly ILogger<TokenProvider> _logger;
private readonly ServerConfigurationManager _serverManager; private readonly ServerConfigurationManager _serverManager;
private readonly RemoteConfigurationService _remoteConfig;
private readonly ConcurrentDictionary<JwtIdentifier, string> _tokenCache = new(); private readonly ConcurrentDictionary<JwtIdentifier, string> _tokenCache = new();
private readonly ConcurrentDictionary<string, string?> _wellKnownCache = new(StringComparer.Ordinal); private readonly ConcurrentDictionary<string, string?> _wellKnownCache = new(StringComparer.Ordinal);
public TokenProvider(ILogger<TokenProvider> logger, ServerConfigurationManager serverManager, RemoteConfigurationService remoteConfig, public TokenProvider(ILogger<TokenProvider> logger, ServerConfigurationManager serverManager,
DalamudUtilService dalamudUtil, MareMediator mareMediator) DalamudUtilService dalamudUtil, MareMediator mareMediator)
{ {
_logger = logger; _logger = logger;
_serverManager = serverManager; _serverManager = serverManager;
_remoteConfig = remoteConfig;
_dalamudUtil = dalamudUtil; _dalamudUtil = dalamudUtil;
_httpClient = new( _httpClient = new(
new HttpClientHandler new HttpClientHandler
@@ -70,23 +68,11 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber
Uri tokenUri; Uri tokenUri;
HttpResponseMessage result; HttpResponseMessage result;
var authApiUrl = _serverManager.CurrentApiUrl;
// Override the API URL used for auth from remote config, if one is available
if (authApiUrl.Equals(ApiController.UmbraServiceUri, StringComparison.Ordinal))
{
var config = await _remoteConfig.GetConfigAsync<HubConnectionConfig>("mainServer").ConfigureAwait(false) ?? new();
if (!string.IsNullOrEmpty(config.ApiUrl))
authApiUrl = config.ApiUrl;
else
authApiUrl = ApiController.UmbraServiceApiUri;
}
try try
{ {
_logger.LogDebug("GetNewToken: Requesting"); _logger.LogDebug("GetNewToken: Requesting");
tokenUri = MareAuth.AuthV2FullPath(new Uri(authApiUrl tokenUri = MareAuth.AuthV2FullPath(new Uri(_serverManager.CurrentApiUrl
.Replace("wss://", "https://", StringComparison.OrdinalIgnoreCase) .Replace("wss://", "https://", StringComparison.OrdinalIgnoreCase)
.Replace("ws://", "http://", StringComparison.OrdinalIgnoreCase))); .Replace("ws://", "http://", StringComparison.OrdinalIgnoreCase)));
var secretKey = _serverManager.GetSecretKey(out _)!; var secretKey = _serverManager.GetSecretKey(out _)!;

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 KiB