add the whole API stuff first iteration
This commit is contained in:
@@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.GameData", "..\..\
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.PlayerWatch", "..\..\Penumbra\Penumbra.PlayerWatch\Penumbra.PlayerWatch.csproj", "{2F26FC2D-03DF-445F-A87B-8708D621E86C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MareSynchronos.API", "..\server\MareSynchronosServer\MareSynchronos.API\MareSynchronos.API.csproj", "{4C92F86D-9C84-4F58-9C1A-671AEBACA256}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -41,6 +43,14 @@ Global
|
||||
{2F26FC2D-03DF-445F-A87B-8708D621E86C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2F26FC2D-03DF-445F-A87B-8708D621E86C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2F26FC2D-03DF-445F-A87B-8708D621E86C}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4C92F86D-9C84-4F58-9C1A-671AEBACA256}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4C92F86D-9C84-4F58-9C1A-671AEBACA256}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4C92F86D-9C84-4F58-9C1A-671AEBACA256}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4C92F86D-9C84-4F58-9C1A-671AEBACA256}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4C92F86D-9C84-4F58-9C1A-671AEBACA256}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4C92F86D-9C84-4F58-9C1A-671AEBACA256}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4C92F86D-9C84-4F58-9C1A-671AEBACA256}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4C92F86D-9C84-4F58-9C1A-671AEBACA256}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -17,48 +17,58 @@ using Penumbra.PlayerWatch;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Configuration;
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.FileCacheDB;
|
||||
|
||||
namespace MareSynchronos.Managers
|
||||
{
|
||||
public class CharacterManager : IDisposable
|
||||
{
|
||||
private readonly ClientState clientState;
|
||||
private readonly Framework framework;
|
||||
private readonly Framework _framework;
|
||||
private readonly ApiController apiController;
|
||||
private readonly ObjectTable objectTable;
|
||||
private readonly IpcManager ipcManager;
|
||||
private readonly FileReplacementFactory factory;
|
||||
private readonly Configuration _pluginConfiguration;
|
||||
private readonly IPlayerWatcher watcher;
|
||||
private Task? playerChangedTask = null;
|
||||
|
||||
public CharacterManager(ClientState clientState, Framework framework, ApiController apiController, ObjectTable objectTable, IpcManager ipcManager, FileReplacementFactory factory)
|
||||
public CharacterManager(ClientState clientState, Framework framework, ApiController apiController, ObjectTable objectTable, IpcManager ipcManager, FileReplacementFactory factory,
|
||||
Configuration pluginConfiguration)
|
||||
{
|
||||
this.clientState = clientState;
|
||||
this.framework = framework;
|
||||
this._framework = framework;
|
||||
this.apiController = apiController;
|
||||
this.objectTable = objectTable;
|
||||
this.ipcManager = ipcManager;
|
||||
this.factory = factory;
|
||||
_pluginConfiguration = pluginConfiguration;
|
||||
watcher = PlayerWatchFactory.Create(framework, clientState, objectTable);
|
||||
clientState.TerritoryChanged += ClientState_TerritoryChanged;
|
||||
framework.Update += Framework_Update;
|
||||
ipcManager.PenumbraRedrawEvent += IpcManager_PenumbraRedrawEvent;
|
||||
}
|
||||
|
||||
private void IpcManager_PenumbraRedrawEvent(object? sender, EventArgs e)
|
||||
private void IpcManager_PenumbraRedrawEvent(object? objectTableIndex, EventArgs e)
|
||||
{
|
||||
var actorName = ((string)sender!);
|
||||
PluginLog.Debug("Penumbra redraw " + actorName);
|
||||
if (actorName == GetPlayerName())
|
||||
var objTableObj = objectTable[(int)objectTableIndex!];
|
||||
if (objTableObj!.ObjectKind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player)
|
||||
{
|
||||
PlayerChanged(actorName);
|
||||
if (objTableObj.Name.ToString() == GetPlayerName())
|
||||
{
|
||||
PluginLog.Debug("Penumbra Redraw Event");
|
||||
PlayerChanged(GetPlayerName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<(string, int), CharacterCacheDto> _characterCache = new();
|
||||
|
||||
Dictionary<string, string> localPlayers = new();
|
||||
private DateTime lastCheck = DateTime.Now;
|
||||
|
||||
@@ -71,13 +81,15 @@ namespace MareSynchronos.Managers
|
||||
if (DateTime.Now < lastCheck.AddSeconds(5)) return;
|
||||
|
||||
List<string> localPlayersList = new();
|
||||
List<string> newPlayers = new();
|
||||
foreach (var obj in objectTable)
|
||||
{
|
||||
if (obj.ObjectKind != Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player) continue;
|
||||
string playerName = obj.Name.ToString();
|
||||
if (playerName == clientState.LocalPlayer.Name.ToString()) continue;
|
||||
var hashedName = Crypto.GetHash(playerName);
|
||||
if (playerName == GetPlayerName()) continue;
|
||||
var pObj = (PlayerCharacter)obj;
|
||||
var hashedName = Crypto.GetHash256(pObj.Name.ToString() + pObj.HomeWorld.Id.ToString());
|
||||
localPlayersList.Add(hashedName);
|
||||
localPlayers[hashedName] = pObj.Name.ToString();
|
||||
}
|
||||
|
||||
foreach (var item in localPlayers.ToList())
|
||||
@@ -88,8 +100,6 @@ namespace MareSynchronos.Managers
|
||||
}
|
||||
}
|
||||
|
||||
if (newPlayers.Any())
|
||||
PluginLog.Debug("New players: " + string.Join(",", newPlayers.Select(p => p + ":" + localPlayers[p])));
|
||||
lastCheck = DateTime.Now;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -128,7 +138,7 @@ namespace MareSynchronos.Managers
|
||||
PluginLog.Debug("Waiting for charater to be drawn");
|
||||
while ((obj->RenderFlags & 0b100000000000) == 0b100000000000) // 0b100000000000 is "still rendering" or something
|
||||
{
|
||||
PluginLog.Debug("Waiting for character to finish drawing");
|
||||
//PluginLog.Debug("Waiting for character to finish drawing");
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
PluginLog.Debug("Character finished drawing");
|
||||
@@ -142,14 +152,34 @@ namespace MareSynchronos.Managers
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
|
||||
_ = apiController.SendCharacterData(cache.Result);
|
||||
_ = apiController.SendCharacterData(cache.Result.ToCharacterCacheDto(), GetLocalPlayers().Select(d => d.Key).ToList());
|
||||
});
|
||||
}
|
||||
|
||||
private Dictionary<string, PlayerCharacter> GetLocalPlayers()
|
||||
{
|
||||
Dictionary<string, PlayerCharacter> allLocalPlayers = new();
|
||||
foreach (var obj in objectTable)
|
||||
{
|
||||
if (obj.ObjectKind != Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player) continue;
|
||||
string playerName = obj.Name.ToString();
|
||||
if (playerName == GetPlayerName()) continue;
|
||||
var playerObject = (PlayerCharacter)obj;
|
||||
allLocalPlayers.Add(Crypto.GetHash256(playerObject.Name.ToString() + playerObject.HomeWorld.Id.ToString()), playerObject);
|
||||
}
|
||||
|
||||
return allLocalPlayers;
|
||||
}
|
||||
|
||||
public unsafe CharacterCache BuildCharacterCache()
|
||||
{
|
||||
var cache = new CharacterCache();
|
||||
|
||||
while (clientState.LocalPlayer == null)
|
||||
{
|
||||
PluginLog.Debug("Character is null but it shouldn't be, waiting");
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
var model = (CharacterBase*)((Character*)clientState.LocalPlayer!.Address)->GameObject.GetDrawObject();
|
||||
for (var idx = 0; idx < model->SlotCount; ++idx)
|
||||
{
|
||||
@@ -213,21 +243,193 @@ namespace MareSynchronos.Managers
|
||||
private void ClientState_TerritoryChanged(object? sender, ushort e)
|
||||
{
|
||||
localPlayers.Clear();
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
while (clientState.LocalPlayer == null)
|
||||
{
|
||||
await Task.Delay(250);
|
||||
}
|
||||
|
||||
await AssignLocalPlayersData();
|
||||
});
|
||||
}
|
||||
|
||||
private async Task AssignLocalPlayersData()
|
||||
{
|
||||
PluginLog.Debug("Temp assigning local players from cache");
|
||||
var currentLocalPlayers = GetLocalPlayers();
|
||||
foreach (var player in _characterCache)
|
||||
{
|
||||
if (currentLocalPlayers.ContainsKey(player.Key.Item1))
|
||||
{
|
||||
await Task.Run(() => ApiControllerOnCharacterReceived(null, new CharacterReceivedEventArgs
|
||||
{
|
||||
CharacterNameHash = player.Key.Item1,
|
||||
CharacterData = player.Value
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
PluginLog.Debug("Updating local players from service");
|
||||
await apiController.GetCharacterData(currentLocalPlayers
|
||||
.ToDictionary(
|
||||
k => k.Key,
|
||||
k => (int)k.Value.ClassJob.Id));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
framework.Update -= Framework_Update;
|
||||
ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent;
|
||||
_framework.Update -= Framework_Update;
|
||||
clientState.TerritoryChanged -= ClientState_TerritoryChanged;
|
||||
apiController.Connected -= ApiController_Connected;
|
||||
apiController.Disconnected -= ApiController_Disconnected;
|
||||
apiController.CharacterReceived -= ApiControllerOnCharacterReceived;
|
||||
apiController.RemovedFromWhitelist -= ApiControllerOnRemovedFromWhitelist;
|
||||
apiController.AddedToWhitelist -= ApiControllerOnAddedToWhitelist;
|
||||
watcher.Disable();
|
||||
watcher.PlayerChanged -= Watcher_PlayerChanged;
|
||||
watcher?.Dispose();
|
||||
}
|
||||
|
||||
internal void StartWatchingPlayer()
|
||||
{
|
||||
watcher.AddPlayerToWatch(clientState.LocalPlayer!.Name.ToString());
|
||||
watcher.AddPlayerToWatch(GetPlayerName());
|
||||
watcher.PlayerChanged += Watcher_PlayerChanged;
|
||||
watcher.Enable();
|
||||
apiController.Connected += ApiController_Connected;
|
||||
apiController.Disconnected += ApiController_Disconnected;
|
||||
apiController.CharacterReceived += ApiControllerOnCharacterReceived;
|
||||
apiController.RemovedFromWhitelist += ApiControllerOnRemovedFromWhitelist;
|
||||
apiController.AddedToWhitelist += ApiControllerOnAddedToWhitelist;
|
||||
|
||||
PluginLog.Debug("Watching Player, ApiController is Connected: " + apiController.IsConnected);
|
||||
if (apiController.IsConnected)
|
||||
{
|
||||
ApiController_Connected(null, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApiControllerOnRemovedFromWhitelist(object? sender, EventArgs e)
|
||||
{
|
||||
var characterHash = (string?)sender;
|
||||
if (string.IsNullOrEmpty(characterHash)) return;
|
||||
var players = GetLocalPlayers();
|
||||
foreach (var entry in _characterCache.Where(c => c.Key.Item1 == characterHash))
|
||||
{
|
||||
_characterCache.Remove(entry.Key);
|
||||
}
|
||||
|
||||
var playerName = players.SingleOrDefault(p => p.Key == characterHash).Value.Name.ToString() ?? null;
|
||||
if (playerName != null)
|
||||
{
|
||||
PluginLog.Debug("You got removed from whitelist, restoring glamourer state for " + playerName);
|
||||
ipcManager.GlamourerRevertCharacterCustomization(playerName);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApiControllerOnAddedToWhitelist(object? sender, EventArgs e)
|
||||
{
|
||||
var characterHash = (string?)sender;
|
||||
if (string.IsNullOrEmpty(characterHash)) return;
|
||||
var players = GetLocalPlayers();
|
||||
if (players.ContainsKey(characterHash))
|
||||
{
|
||||
PluginLog.Debug("You got added to a whitelist, restoring data for " + characterHash);
|
||||
_ = apiController.GetCharacterData(new Dictionary<string, int> { { characterHash, (int)players[characterHash].ClassJob.Id } });
|
||||
}
|
||||
}
|
||||
|
||||
private void ApiControllerOnCharacterReceived(object? sender, CharacterReceivedEventArgs e)
|
||||
{
|
||||
PlayerCharacter? playerObject = null;
|
||||
PluginLog.Debug("Received hash for " + e.CharacterNameHash);
|
||||
foreach (var obj in objectTable)
|
||||
{
|
||||
if (obj.ObjectKind != Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Player) continue;
|
||||
string playerName = obj.Name.ToString();
|
||||
if (playerName == GetPlayerName()) continue;
|
||||
playerObject = (PlayerCharacter)obj;
|
||||
var hashedName = Crypto.GetHash256(playerObject.Name.ToString() + playerObject.HomeWorld.Id.ToString());
|
||||
if (e.CharacterNameHash == hashedName)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
playerObject = null;
|
||||
}
|
||||
|
||||
if (playerObject == null)
|
||||
{
|
||||
PluginLog.Debug("Found no suitable hash for " + e.CharacterNameHash);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
PluginLog.Debug("Found suitable player for hash: " + playerObject.Name.ToString());
|
||||
}
|
||||
|
||||
_characterCache[(e.CharacterNameHash, e.CharacterData.JobId)] = e.CharacterData;
|
||||
|
||||
foreach (var file in e.CharacterData.FileReplacements)
|
||||
{
|
||||
var hash = file.Hash;
|
||||
bool hasLocalFile;
|
||||
using (var db = new FileCacheContext())
|
||||
{
|
||||
hasLocalFile = db.FileCaches.Any(f => f.Hash == hash);
|
||||
}
|
||||
|
||||
if (hasLocalFile) continue;
|
||||
PluginLog.Debug("Downloading file for " + hash);
|
||||
var task = apiController.DownloadData(hash);
|
||||
while (!task.IsCompleted)
|
||||
{
|
||||
Thread.Sleep(TimeSpan.FromSeconds(0.5));
|
||||
}
|
||||
PluginLog.Debug("Download finished: " + hash);
|
||||
var extractedFile = LZ4.LZ4Codec.Unwrap(task.Result);
|
||||
var ext = file.GamePaths.First().Split(".", StringSplitOptions.None).Last();
|
||||
var filePath = Path.Combine(_pluginConfiguration.CacheFolder, file.Hash + "." + ext);
|
||||
File.WriteAllBytes(filePath, extractedFile);
|
||||
PluginLog.Debug("File written to : " + filePath);
|
||||
using (var db = new FileCacheContext())
|
||||
{
|
||||
db.Add(new FileCache
|
||||
{
|
||||
Filepath = filePath.ToLower(),
|
||||
Hash = file.Hash,
|
||||
LastModifiedDate = DateTime.Now.Ticks.ToString(),
|
||||
});
|
||||
db.SaveChanges();
|
||||
}
|
||||
PluginLog.Debug("Added hash to db: " + hash);
|
||||
}
|
||||
|
||||
PluginLog.Debug("Assigned hash to visible player: " + playerObject.Name.ToString());
|
||||
ipcManager.GlamourerApplyCharacterCustomization(e.CharacterData.GlamourerData, playerObject.Name.ToString());
|
||||
}
|
||||
|
||||
private void ApiController_Disconnected(object? sender, EventArgs args)
|
||||
{
|
||||
PluginLog.Debug(nameof(ApiController_Disconnected));
|
||||
_framework.Update -= Framework_Update;
|
||||
ipcManager.PenumbraRedrawEvent -= IpcManager_PenumbraRedrawEvent;
|
||||
clientState.TerritoryChanged -= ClientState_TerritoryChanged;
|
||||
}
|
||||
|
||||
private void ApiController_Connected(object? sender, EventArgs args)
|
||||
{
|
||||
PluginLog.Debug(nameof(ApiController_Connected));
|
||||
PluginLog.Debug("MyHashedName:" + Crypto.GetHash256(GetPlayerName() + clientState.LocalPlayer!.HomeWorld.Id));
|
||||
var apiTask = apiController.SendCharacterName(Crypto.GetHash256(GetPlayerName() + clientState.LocalPlayer!.HomeWorld.Id));
|
||||
var assignTask = AssignLocalPlayersData();
|
||||
|
||||
Task.WaitAll(apiTask, assignTask);
|
||||
|
||||
_framework.Update += Framework_Update;
|
||||
ipcManager.PenumbraRedrawEvent += IpcManager_PenumbraRedrawEvent;
|
||||
clientState.TerritoryChanged += ClientState_TerritoryChanged;
|
||||
}
|
||||
|
||||
public void StopWatchPlayer(string name)
|
||||
|
||||
@@ -15,9 +15,10 @@ namespace MareSynchronos.Managers
|
||||
private ICallGateSubscriber<string, string, object>? glamourerApplyCharacterCustomization;
|
||||
private ICallGateSubscriber<int> penumbraApiVersion;
|
||||
private ICallGateSubscriber<int> glamourerApiVersion;
|
||||
private ICallGateSubscriber<string, string> penumbraObjectIsRedrawn;
|
||||
private ICallGateSubscriber<IntPtr, int, object?> penumbraObjectIsRedrawn;
|
||||
private ICallGateSubscriber<string, int, object>? penumbraRedraw;
|
||||
private ICallGateSubscriber<string, string, string[]>? penumbraReverseResolvePath;
|
||||
private ICallGateSubscriber<string, object> glamourerRevertCustomization;
|
||||
|
||||
public bool Initialized { get; private set; } = false;
|
||||
|
||||
@@ -36,7 +37,8 @@ namespace MareSynchronos.Managers
|
||||
penumbraReverseResolvePath = pluginInterface.GetIpcSubscriber<string, string, string[]>("Penumbra.ReverseResolvePath");
|
||||
penumbraApiVersion = pluginInterface.GetIpcSubscriber<int>("Penumbra.ApiVersion");
|
||||
glamourerApiVersion = pluginInterface.GetIpcSubscriber<int>("Glamourer.ApiVersion");
|
||||
penumbraObjectIsRedrawn = pluginInterface.GetIpcSubscriber<string, string>("Penumbra.ObjectIsRedrawn");
|
||||
glamourerRevertCustomization = pluginInterface.GetIpcSubscriber<string, object>("Glamourer.RevertCharacterCustomization");
|
||||
penumbraObjectIsRedrawn = pluginInterface.GetIpcSubscriber<IntPtr, int, object?>("Penumbra.GameObjectRedrawn");
|
||||
penumbraObjectIsRedrawn.Subscribe(RedrawEvent);
|
||||
penumbraInit.Subscribe(RedrawSelf);
|
||||
|
||||
@@ -67,9 +69,9 @@ namespace MareSynchronos.Managers
|
||||
}
|
||||
}
|
||||
|
||||
private void RedrawEvent(string actorName)
|
||||
private void RedrawEvent(IntPtr objectAddress, int objectTableIndex)
|
||||
{
|
||||
PenumbraRedrawEvent?.Invoke(actorName, EventArgs.Empty);
|
||||
PenumbraRedrawEvent?.Invoke(objectTableIndex, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void RedrawSelf()
|
||||
@@ -120,6 +122,12 @@ namespace MareSynchronos.Managers
|
||||
glamourerApplyCharacterCustomization!.InvokeAction(customization, characterName);
|
||||
}
|
||||
|
||||
public void GlamourerRevertCharacterCustomization(string characterName)
|
||||
{
|
||||
if (!CheckGlamourerAPI()) return;
|
||||
glamourerRevertCustomization!.InvokeAction(characterName);
|
||||
}
|
||||
|
||||
public void PenumbraRedraw(string actorName)
|
||||
{
|
||||
if (!CheckPenumbraAPI()) return;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DalamudPackager" Version="2.1.7" />
|
||||
<PackageReference Include="lz4net" Version="1.0.15.93" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.17">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -39,6 +40,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Penumbra\Penumbra.GameData\Penumbra.GameData.csproj" />
|
||||
<ProjectReference Include="..\..\..\Penumbra\Penumbra.PlayerWatch\Penumbra.PlayerWatch.csproj" />
|
||||
<ProjectReference Include="..\..\server\MareSynchronosServer\MareSynchronos.API\MareSynchronos.API.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -5,12 +5,24 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MareSynchronos.API;
|
||||
|
||||
namespace MareSynchronos.Models
|
||||
{
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public class CharacterCache
|
||||
{
|
||||
public CharacterCacheDto ToCharacterCacheDto()
|
||||
{
|
||||
return new CharacterCacheDto()
|
||||
{
|
||||
FileReplacements = AllReplacements.Select(f => f.ToFileReplacementDto()).ToList(),
|
||||
GlamourerData = GlamourerString,
|
||||
Hash = CacheHash,
|
||||
JobId = (int)JobId
|
||||
};
|
||||
}
|
||||
|
||||
[JsonProperty]
|
||||
public List<FileReplacement> AllReplacements =>
|
||||
FileReplacements.Where(f => f.HasFileReplacement)
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using MareSynchronos.FileCacheDB;
|
||||
using System.IO;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.Utils;
|
||||
|
||||
namespace MareSynchronos.Models
|
||||
@@ -14,6 +15,16 @@ namespace MareSynchronos.Models
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public class FileReplacement
|
||||
{
|
||||
public FileReplacementDto ToFileReplacementDto()
|
||||
{
|
||||
return new FileReplacementDto
|
||||
{
|
||||
GamePaths = GamePaths,
|
||||
Hash = Hash,
|
||||
ImcData = ImcData
|
||||
};
|
||||
}
|
||||
|
||||
private readonly string penumbraDirectory;
|
||||
|
||||
[JsonProperty]
|
||||
@@ -88,13 +99,20 @@ namespace MareSynchronos.Models
|
||||
var fileAddedDuringCompute = db.FileCaches.SingleOrDefault(f => f.Filepath == fi.FullName.ToLower());
|
||||
if (fileAddedDuringCompute != null) return fileAddedDuringCompute.Hash;
|
||||
|
||||
db.Add(new FileCache()
|
||||
try
|
||||
{
|
||||
Hash = hash,
|
||||
Filepath = fi.FullName.ToLower(),
|
||||
LastModifiedDate = fi.LastWriteTimeUtc.Ticks.ToString()
|
||||
});
|
||||
db.SaveChanges();
|
||||
db.Add(new FileCache()
|
||||
{
|
||||
Hash = hash,
|
||||
Filepath = fi.FullName.ToLower(),
|
||||
LastModifiedDate = fi.LastWriteTimeUtc.Ticks.ToString()
|
||||
});
|
||||
db.SaveChanges();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, "Error adding files to database. Most likely not an issue though.");
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace MareSynchronos
|
||||
}
|
||||
|
||||
characterManager = new CharacterManager(
|
||||
clientState, framework, apiController, objectTable, ipcManager, new FileReplacementFactory(ipcManager));
|
||||
clientState, framework, apiController, objectTable, ipcManager, new FileReplacementFactory(ipcManager), Configuration);
|
||||
characterManager.StartWatchingPlayer();
|
||||
ipcManager.PenumbraRedraw(clientState.LocalPlayer!.Name.ToString());
|
||||
});
|
||||
@@ -131,7 +131,7 @@ namespace MareSynchronos
|
||||
{
|
||||
|
||||
Stopwatch st = Stopwatch.StartNew();
|
||||
File.WriteAllBytes(lc4hcPath, LZ4Codec.Encode(File.ReadAllBytes(fileCache.Filepath), 0, (int)new FileInfo(fileCache.Filepath).Length));
|
||||
File.WriteAllBytes(lc4hcPath, LZ4Codec.WrapHC(File.ReadAllBytes(fileCache.Filepath), 0, (int)new FileInfo(fileCache.Filepath).Length));
|
||||
st.Stop();
|
||||
PluginLog.Debug("Compressed " + new FileInfo(fileCache.Filepath).Length + " bytes to " + new FileInfo(lc4hcPath).Length + " bytes in " + st.Elapsed);
|
||||
File.Copy(fileCache.Filepath, newFilePath);
|
||||
@@ -226,6 +226,11 @@ namespace MareSynchronos
|
||||
PluginLog.Debug("Mod created to " + modDirectoryPath);
|
||||
});
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(args))
|
||||
{
|
||||
PluginUi.Toggle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,10 @@ using ImGuiNET;
|
||||
using MareSynchronos.Managers;
|
||||
using MareSynchronos.WebAPI;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Configuration;
|
||||
using MareSynchronos.API;
|
||||
|
||||
namespace MareSynchronos
|
||||
{
|
||||
@@ -49,7 +52,17 @@ namespace MareSynchronos
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(apiController.SecretKey))
|
||||
|
||||
if (apiController.SecretKey != "-" && !apiController.IsConnected && apiController.ServerAlive)
|
||||
{
|
||||
if (ImGui.Button("Reset Secret Key"))
|
||||
{
|
||||
configuration.ClientSecret.Clear();
|
||||
configuration.Save();
|
||||
apiController.RestartHeartbeat();
|
||||
}
|
||||
}
|
||||
else if (apiController.SecretKey == "-")
|
||||
{
|
||||
DrawIntroContent();
|
||||
}
|
||||
@@ -67,15 +80,95 @@ namespace MareSynchronos
|
||||
ImGui.Separator();
|
||||
ImGui.Text("Your UID");
|
||||
ImGui.SameLine();
|
||||
ImGui.TextColored(ImGuiColors.ParsedGreen, apiController.UID);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Copy UID"))
|
||||
if (apiController.ServerAlive)
|
||||
{
|
||||
ImGui.SetClipboardText(apiController.UID);
|
||||
ImGui.TextColored(ImGuiColors.ParsedGreen, apiController.UID);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Copy UID"))
|
||||
{
|
||||
ImGui.SetClipboardText(apiController.UID);
|
||||
}
|
||||
ImGui.Text("Share this UID to other Mare users so they can add you to their whitelist.");
|
||||
ImGui.Separator();
|
||||
DrawWhiteListContent();
|
||||
ImGui.Separator();
|
||||
string cachePath = configuration.CacheFolder;
|
||||
if (ImGui.InputText("CachePath", ref cachePath, 255))
|
||||
{
|
||||
configuration.CacheFolder = cachePath;
|
||||
configuration.Save();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextColored(ImGuiColors.DalamudRed, "Service unavailable");
|
||||
}
|
||||
ImGui.Text("Share this UID to other Mare users so they can add you to their whitelist.");
|
||||
}
|
||||
|
||||
private void DrawWhiteListContent()
|
||||
{
|
||||
if (!apiController.ServerAlive) return;
|
||||
ImGui.Text("Whitelists");
|
||||
if (ImGui.BeginTable("WhitelistTable", 5))
|
||||
{
|
||||
ImGui.TableSetupColumn("Pause");
|
||||
ImGui.TableSetupColumn("UID");
|
||||
ImGui.TableSetupColumn("Sync");
|
||||
ImGui.TableSetupColumn("Paused (you/other)");
|
||||
ImGui.TableSetupColumn("");
|
||||
ImGui.TableHeadersRow();
|
||||
foreach (var item in apiController.WhitelistEntries.ToList())
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
bool isPaused = item.IsPaused;
|
||||
if (ImGui.Checkbox("Paused##" + item.OtherUID, ref isPaused))
|
||||
{
|
||||
_ = apiController.SendWhitelistPauseChange(item.OtherUID, isPaused);
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextColored(GetBoolColor(item.IsSynced && !item.IsPausedFromOthers && !item.IsPaused),
|
||||
item.OtherUID);
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextColored(GetBoolColor(item.IsSynced), !item.IsSynced ? "Has not added you" : "On both whitelists");
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextColored(GetBoolColor((!item.IsPausedFromOthers && !item.IsPaused)), item.IsPaused + " / " + item.IsPausedFromOthers);
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Button("Delete##" + item.OtherUID))
|
||||
{
|
||||
_ = apiController.SendWhitelistRemoval(item.OtherUID);
|
||||
apiController.WhitelistEntries.Remove(item);
|
||||
}
|
||||
ImGui.TableNextRow();
|
||||
}
|
||||
ImGui.EndTable();
|
||||
}
|
||||
|
||||
var whitelistEntry = tempDto.OtherUID;
|
||||
if (ImGui.InputText("Add new whitelist entry", ref whitelistEntry, 20))
|
||||
{
|
||||
tempDto.OtherUID = whitelistEntry;
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Add"))
|
||||
{
|
||||
if (apiController.WhitelistEntries.All(w => w.OtherUID != tempDto.OtherUID))
|
||||
{
|
||||
apiController.WhitelistEntries.Add(new WhitelistDto()
|
||||
{
|
||||
OtherUID = tempDto.OtherUID
|
||||
});
|
||||
|
||||
_ = apiController.SendWhitelistAddition(tempDto.OtherUID);
|
||||
|
||||
tempDto.OtherUID = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private WhitelistDto tempDto = new WhitelistDto() { OtherUID = string.Empty };
|
||||
|
||||
private int serverSelectionIndex = 0;
|
||||
|
||||
private async void DrawIntroContent()
|
||||
@@ -141,13 +234,19 @@ namespace MareSynchronos
|
||||
if (apiController.UseCustomService)
|
||||
{
|
||||
string serviceAddress = configuration.ApiUri;
|
||||
ImGui.InputText("Service address", ref serviceAddress, 255);
|
||||
configuration.ApiUri = serviceAddress;
|
||||
configuration.Save();
|
||||
if (ImGui.InputText("Service address", ref serviceAddress, 255))
|
||||
{
|
||||
if (configuration.ApiUri != serviceAddress)
|
||||
{
|
||||
configuration.ApiUri = serviceAddress;
|
||||
apiController.RestartHeartbeat();
|
||||
configuration.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrintServerState();
|
||||
if (apiController.IsConnected)
|
||||
if (apiController.ServerAlive)
|
||||
{
|
||||
if (ImGui.Button("Register"))
|
||||
{
|
||||
@@ -159,6 +258,8 @@ namespace MareSynchronos
|
||||
}
|
||||
}
|
||||
|
||||
private Vector4 GetBoolColor(bool input) => input ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed;
|
||||
|
||||
private bool OtherPluginStateOk()
|
||||
{
|
||||
var penumbraExists = ipcManager.CheckPenumbraAPI();
|
||||
@@ -186,8 +287,8 @@ namespace MareSynchronos
|
||||
{
|
||||
ImGui.Text("Service status of " + (string.IsNullOrEmpty(configuration.ApiUri) ? mainServer : configuration.ApiUri));
|
||||
ImGui.SameLine();
|
||||
var color = apiController.IsConnected ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed;
|
||||
ImGui.TextColored(color, apiController.IsConnected ? "Available" : "Unavailable");
|
||||
var color = apiController.ServerAlive ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed;
|
||||
ImGui.TextColored(color, apiController.ServerAlive ? "Available" : "Unavailable");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,5 +18,11 @@ namespace MareSynchronos.Utils
|
||||
using SHA1CryptoServiceProvider cryptoProvider = new();
|
||||
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToHash))).Replace("-", "");
|
||||
}
|
||||
|
||||
public static string GetHash256(string stringToHash)
|
||||
{
|
||||
using SHA256CryptoServiceProvider cryptoProvider = new();
|
||||
return BitConverter.ToString(cryptoProvider.ComputeHash(Encoding.UTF8.GetBytes(stringToHash))).Replace("-", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,20 +2,38 @@
|
||||
using Dalamud.Logging;
|
||||
using MareSynchronos.Models;
|
||||
using System;
|
||||
using System.Buffers.Text;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.InteropServices.ComTypes;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using LZ4;
|
||||
using MareSynchronos.API;
|
||||
using MareSynchronos.FileCacheDB;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
|
||||
namespace MareSynchronos.WebAPI
|
||||
{
|
||||
public class CharacterReceivedEventArgs : EventArgs
|
||||
{
|
||||
public CharacterCacheDto CharacterData { get; set; }
|
||||
public string CharacterNameHash { get; set; }
|
||||
}
|
||||
|
||||
public class ApiController : IDisposable
|
||||
{
|
||||
private readonly Configuration pluginConfiguration;
|
||||
private const string mainService = "https://localhost:6591";
|
||||
private const string MainService = "https://darkarchon.internet-box.ch:5001";
|
||||
public string UID { get; private set; } = string.Empty;
|
||||
public string SecretKey => pluginConfiguration.ClientSecret.ContainsKey(ApiUri) ? pluginConfiguration.ClientSecret[ApiUri] : string.Empty;
|
||||
public string SecretKey => pluginConfiguration.ClientSecret.ContainsKey(ApiUri) ? pluginConfiguration.ClientSecret[ApiUri] : "-";
|
||||
private string CacheFolder => pluginConfiguration.CacheFolder;
|
||||
public bool UseCustomService
|
||||
{
|
||||
@@ -23,89 +41,295 @@ namespace MareSynchronos.WebAPI
|
||||
set
|
||||
{
|
||||
pluginConfiguration.UseCustomService = value;
|
||||
_ = Heartbeat();
|
||||
pluginConfiguration.Save();
|
||||
}
|
||||
}
|
||||
private string ApiUri => UseCustomService ? pluginConfiguration.ApiUri : mainService;
|
||||
private string ApiUri => UseCustomService ? pluginConfiguration.ApiUri : MainService;
|
||||
|
||||
public bool IsConnected { get; set; }
|
||||
public bool ServerAlive =>
|
||||
(_heartbeatHub?.State ?? HubConnectionState.Disconnected) == HubConnectionState.Connected;
|
||||
public bool IsConnected => !string.IsNullOrEmpty(UID);
|
||||
|
||||
Task heartbeatTask;
|
||||
CancellationTokenSource cts;
|
||||
public event EventHandler? Connected;
|
||||
public event EventHandler? Disconnected;
|
||||
public event EventHandler<CharacterReceivedEventArgs>? CharacterReceived;
|
||||
public event EventHandler? RemovedFromWhitelist;
|
||||
public event EventHandler? AddedToWhitelist;
|
||||
|
||||
public List<WhitelistDto> WhitelistEntries { get; set; } = new List<WhitelistDto>();
|
||||
|
||||
readonly CancellationTokenSource cts;
|
||||
private HubConnection? _heartbeatHub;
|
||||
private IDisposable? _fileUploadRequest;
|
||||
private HubConnection? _fileHub;
|
||||
private HubConnection? _userHub;
|
||||
|
||||
public ApiController(Configuration pluginConfiguration)
|
||||
{
|
||||
this.pluginConfiguration = pluginConfiguration;
|
||||
cts = new CancellationTokenSource();
|
||||
|
||||
heartbeatTask = Task.Run(async () =>
|
||||
{
|
||||
PluginLog.Debug("Starting heartbeat to " + ApiUri);
|
||||
while (true && !cts.IsCancellationRequested)
|
||||
{
|
||||
await Heartbeat();
|
||||
await Task.Delay(TimeSpan.FromSeconds(15), cts.Token);
|
||||
}
|
||||
PluginLog.Debug("Stopping heartbeat");
|
||||
}, cts.Token);
|
||||
_ = Heartbeat();
|
||||
}
|
||||
|
||||
public async Task Heartbeat()
|
||||
{
|
||||
try
|
||||
while (!ServerAlive && !cts.Token.IsCancellationRequested)
|
||||
{
|
||||
PluginLog.Debug("Sending heartbeat to " + ApiUri);
|
||||
if (ApiUri != mainService) throw new Exception();
|
||||
IsConnected = true;
|
||||
try
|
||||
{
|
||||
PluginLog.Debug("Attempting to establish heartbeat connection to " + ApiUri);
|
||||
_heartbeatHub = new HubConnectionBuilder()
|
||||
.WithUrl(ApiUri + "/heartbeat", options =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(SecretKey))
|
||||
{
|
||||
options.Headers.Add("Authorization", SecretKey);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
options.HttpMessageHandlerFactory = (message) =>
|
||||
{
|
||||
if (message is HttpClientHandler clientHandler)
|
||||
clientHandler.ServerCertificateCustomValidationCallback +=
|
||||
(sender, certificate, chain, sslPolicyErrors) => true;
|
||||
return message;
|
||||
};
|
||||
#endif
|
||||
}).Build();
|
||||
PluginLog.Debug("Heartbeat service built to: " + ApiUri);
|
||||
|
||||
await _heartbeatHub.StartAsync(cts.Token);
|
||||
UID = await _heartbeatHub!.InvokeAsync<string>("Heartbeat");
|
||||
PluginLog.Debug("Heartbeat started: " + ApiUri);
|
||||
try
|
||||
{
|
||||
await InitializeHubConnections();
|
||||
await LoadInitialData();
|
||||
Connected?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, "Error during Heartbeat initialization");
|
||||
}
|
||||
|
||||
_heartbeatHub.Closed += OnHeartbeatHubOnClosed;
|
||||
_heartbeatHub.Reconnected += OnHeartbeatHubOnReconnected;
|
||||
PluginLog.Debug("Heartbeat established to: " + ApiUri);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PluginLog.Error(ex, "Creating heartbeat failure");
|
||||
}
|
||||
}
|
||||
catch
|
||||
}
|
||||
|
||||
private async Task LoadInitialData()
|
||||
{
|
||||
var whiteList = await _userHub!.InvokeAsync<List<WhitelistDto>>("GetWhitelist");
|
||||
WhitelistEntries = whiteList.ToList();
|
||||
}
|
||||
|
||||
public void RestartHeartbeat()
|
||||
{
|
||||
PluginLog.Debug("Restarting heartbeat");
|
||||
|
||||
_heartbeatHub!.Closed -= OnHeartbeatHubOnClosed;
|
||||
_heartbeatHub!.Reconnected -= OnHeartbeatHubOnReconnected;
|
||||
Task.Run(async () =>
|
||||
{
|
||||
IsConnected = false;
|
||||
await _heartbeatHub.StopAsync(cts.Token);
|
||||
await _heartbeatHub.DisposeAsync();
|
||||
_heartbeatHub = null!;
|
||||
_ = Heartbeat();
|
||||
});
|
||||
}
|
||||
|
||||
private async Task OnHeartbeatHubOnReconnected(string? s)
|
||||
{
|
||||
PluginLog.Debug("Reconnected: " + ApiUri);
|
||||
UID = await _heartbeatHub!.InvokeAsync<string>("Heartbeat");
|
||||
}
|
||||
|
||||
private Task OnHeartbeatHubOnClosed(Exception? exception)
|
||||
{
|
||||
PluginLog.Debug("Connection closed: " + ApiUri);
|
||||
Disconnected?.Invoke(null, EventArgs.Empty);
|
||||
RestartHeartbeat();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task DisposeHubConnections()
|
||||
{
|
||||
if (_fileHub != null)
|
||||
{
|
||||
PluginLog.Debug("Disposing File Hub");
|
||||
_fileUploadRequest?.Dispose();
|
||||
await _fileHub!.StopAsync();
|
||||
await _fileHub!.DisposeAsync();
|
||||
}
|
||||
|
||||
if (_userHub != null)
|
||||
{
|
||||
PluginLog.Debug("Disposing User Hub");
|
||||
await _userHub.StopAsync();
|
||||
await _userHub.DisposeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InitializeHubConnections()
|
||||
{
|
||||
await DisposeHubConnections();
|
||||
|
||||
PluginLog.Debug("Creating User Hub");
|
||||
_userHub = new HubConnectionBuilder()
|
||||
.WithUrl(ApiUri + "/user", options =>
|
||||
{
|
||||
options.Headers.Add("Authorization", SecretKey);
|
||||
#if DEBUG
|
||||
options.HttpMessageHandlerFactory = (message) =>
|
||||
{
|
||||
if (message is HttpClientHandler clientHandler)
|
||||
clientHandler.ServerCertificateCustomValidationCallback +=
|
||||
(sender, certificate, chain, sslPolicyErrors) => true;
|
||||
return message;
|
||||
};
|
||||
#endif
|
||||
})
|
||||
.Build();
|
||||
await _userHub.StartAsync();
|
||||
_userHub.On<WhitelistDto, string>("UpdateWhitelist", UpdateLocalWhitelist);
|
||||
_userHub.On<CharacterCacheDto, string>("ReceiveCharacterData", ReceiveCharacterData);
|
||||
|
||||
PluginLog.Debug("Creating File Hub");
|
||||
_fileHub = new HubConnectionBuilder()
|
||||
.WithUrl(ApiUri + "/files", options =>
|
||||
{
|
||||
options.Headers.Add("Authorization", SecretKey);
|
||||
#if DEBUG
|
||||
options.HttpMessageHandlerFactory = (message) =>
|
||||
{
|
||||
if (message is HttpClientHandler clientHandler)
|
||||
clientHandler.ServerCertificateCustomValidationCallback +=
|
||||
(sender, certificate, chain, sslPolicyErrors) => true;
|
||||
return message;
|
||||
};
|
||||
#endif
|
||||
})
|
||||
.Build();
|
||||
await _fileHub.StartAsync(cts.Token);
|
||||
|
||||
_fileUploadRequest = _fileHub!.On<string>("FileRequest", UploadFile);
|
||||
}
|
||||
|
||||
private void UpdateLocalWhitelist(WhitelistDto dto, string characterIdentifier)
|
||||
{
|
||||
var entry = WhitelistEntries.SingleOrDefault(e => e.OtherUID == dto.OtherUID);
|
||||
if (entry == null)
|
||||
{
|
||||
RemovedFromWhitelist?.Invoke(characterIdentifier, EventArgs.Empty);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((entry.IsPausedFromOthers != dto.IsPausedFromOthers || entry.IsSynced != dto.IsSynced || entry.IsPaused != dto.IsPaused)
|
||||
&& !dto.IsPaused && dto.IsSynced && !dto.IsPausedFromOthers)
|
||||
{
|
||||
AddedToWhitelist?.Invoke(characterIdentifier, EventArgs.Empty);
|
||||
}
|
||||
|
||||
entry.IsPaused = dto.IsPaused;
|
||||
entry.IsPausedFromOthers = dto.IsPausedFromOthers;
|
||||
entry.IsSynced = dto.IsSynced;
|
||||
|
||||
if (dto.IsPaused || dto.IsPausedFromOthers || !dto.IsSynced)
|
||||
{
|
||||
RemovedFromWhitelist?.Invoke(characterIdentifier, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UploadFile(string fileHash)
|
||||
{
|
||||
PluginLog.Debug("Requested fileHash: " + fileHash);
|
||||
|
||||
await using var db = new FileCacheContext();
|
||||
var fileCache = db.FileCaches.First(f => f.Hash == fileHash);
|
||||
var compressedFile = LZ4Codec.WrapHC(await File.ReadAllBytesAsync(fileCache.Filepath), 0,
|
||||
(int)new FileInfo(fileCache.Filepath).Length);
|
||||
var response = await _fileHub!.InvokeAsync<bool>("UploadFile", fileHash, compressedFile, cts.Token);
|
||||
PluginLog.Debug("Success: " + response);
|
||||
}
|
||||
|
||||
public async Task Register()
|
||||
{
|
||||
if (!ServerAlive) return;
|
||||
PluginLog.Debug("Registering at service " + ApiUri);
|
||||
var response = ("RandomSecretKey", "RandomUID");
|
||||
pluginConfiguration.ClientSecret[ApiUri] = response.Item1;
|
||||
UID = response.Item2;
|
||||
PluginLog.Debug(pluginConfiguration.ClientSecret[ApiUri]);
|
||||
// pluginConfiguration.Save();
|
||||
var response = await _userHub!.InvokeAsync<string>("Register");
|
||||
pluginConfiguration.ClientSecret[ApiUri] = response;
|
||||
pluginConfiguration.Save();
|
||||
RestartHeartbeat();
|
||||
}
|
||||
|
||||
public async Task UploadFile(string filePath)
|
||||
{
|
||||
PluginLog.Debug("Uploading file " + filePath + " to " + ApiUri);
|
||||
}
|
||||
|
||||
public async Task<byte[]> DownloadFile(string hash)
|
||||
{
|
||||
PluginLog.Debug("Downloading file from service " + ApiUri);
|
||||
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
public async Task<List<string>> SendCharacterData(CharacterCache character)
|
||||
public async Task SendCharacterData(CharacterCacheDto character, List<string> visibleCharacterIds)
|
||||
{
|
||||
if (!IsConnected || SecretKey == "-") return;
|
||||
PluginLog.Debug("Sending Character data to service " + ApiUri);
|
||||
|
||||
List<string> list = new();
|
||||
return list;
|
||||
await _fileHub!.InvokeAsync("SendFiles", character.FileReplacements, cts.Token);
|
||||
|
||||
while (await _fileHub!.InvokeAsync<bool>("IsUploadFinished"))
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(0.5));
|
||||
PluginLog.Debug("Waiting for uploads to finish");
|
||||
}
|
||||
|
||||
await _userHub!.InvokeAsync("PushCharacterData", character, visibleCharacterIds);
|
||||
}
|
||||
|
||||
public async Task<CharacterCache> GetCharacterData(string uid)
|
||||
public Task ReceiveCharacterData(CharacterCacheDto character, string characterHash)
|
||||
{
|
||||
PluginLog.Debug("Getting character data for " + uid + " from service " + ApiUri);
|
||||
PluginLog.Debug("Received DTO for " + characterHash);
|
||||
CharacterReceived?.Invoke(null, new CharacterReceivedEventArgs()
|
||||
{
|
||||
CharacterData = character,
|
||||
CharacterNameHash = characterHash
|
||||
});
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
CharacterCache characterCache = new();
|
||||
return characterCache;
|
||||
public async Task<byte[]> DownloadData(string hash)
|
||||
{
|
||||
return await _fileHub!.InvokeAsync<byte[]>("DownloadFile", hash);
|
||||
}
|
||||
|
||||
public async Task GetCharacterData(Dictionary<string, int> hashedCharacterNames)
|
||||
{
|
||||
await _userHub!.InvokeAsync("GetCharacterData",
|
||||
hashedCharacterNames);
|
||||
}
|
||||
|
||||
public async Task SendWhitelistPauseChange(string uid, bool paused)
|
||||
{
|
||||
if (!IsConnected || SecretKey == "-") return;
|
||||
await _userHub!.SendAsync("SendWhitelistPauseChange", uid, paused);
|
||||
}
|
||||
|
||||
public async Task SendWhitelistAddition(string uid)
|
||||
{
|
||||
if (!IsConnected || SecretKey == "-") return;
|
||||
await _userHub!.SendAsync("SendWhitelistAddition", uid);
|
||||
}
|
||||
|
||||
public async Task SendWhitelistRemoval(string uid)
|
||||
{
|
||||
if (!IsConnected || SecretKey == "-") return;
|
||||
await _userHub!.SendAsync("SendWhitelistRemoval", uid);
|
||||
}
|
||||
|
||||
public async Task SendWhitelist()
|
||||
{
|
||||
PluginLog.Debug("Sending whitelist to service " + ApiUri);
|
||||
if (!IsConnected || SecretKey == "-") return;
|
||||
await _userHub!.SendAsync("SendWhitelist", WhitelistEntries.ToList());
|
||||
WhitelistEntries = (await _userHub!.InvokeAsync<List<WhitelistDto>>("GetWhitelist")).ToList();
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetWhitelist()
|
||||
@@ -119,6 +343,18 @@ namespace MareSynchronos.WebAPI
|
||||
public void Dispose()
|
||||
{
|
||||
cts?.Cancel();
|
||||
_ = DisposeHubConnections();
|
||||
}
|
||||
|
||||
public async Task SendCharacterName(string hashedName)
|
||||
{
|
||||
await _userHub!.SendAsync("SendCharacterNameHash", hashedName);
|
||||
}
|
||||
|
||||
public async Task SendVisibilityData(List<string> visibilities)
|
||||
{
|
||||
if (!IsConnected) return;
|
||||
await _userHub!.SendAsync("SendVisibilityList", visibilities);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user