Skip to content

Commit

Permalink
Add ClientStatePluginScoped (#1384)
Browse files Browse the repository at this point in the history
* Add ClientStatePluginScoped

* Restore InvokeSafely

* Add InvokeSafely for basic Action types.

* Set delegates to null to prevent leaking memory

* Resolve Merge
  • Loading branch information
MidoriKami authored Sep 22, 2023
1 parent 201a927 commit b742abe
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 37 deletions.
137 changes: 119 additions & 18 deletions Dalamud/Game/ClientState/ClientState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,25 @@
using Dalamud.Hooking;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Logging.Internal;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.Game;
using Serilog;
using Lumina.Excel.GeneratedSheets;

using Action = System.Action;

namespace Dalamud.Game.ClientState;

/// <summary>
/// This class represents the state of the game client at the time of access.
/// </summary>
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.BlockingEarlyLoadedService]
#pragma warning disable SA1015
[ResolveVia<IClientState>]
#pragma warning restore SA1015
internal sealed class ClientState : IDisposable, IServiceType, IClientState
{
private static readonly ModuleLog Log = new("ClientState");

private readonly GameLifecycle lifecycle;
private readonly ClientStateAddressResolver address;
private readonly Hook<SetupTerritoryTypeDelegate> setupTerritoryTypeHook;
Expand All @@ -37,7 +38,7 @@ internal sealed class ClientState : IDisposable, IServiceType, IClientState
private readonly NetworkHandlers networkHandlers = Service<NetworkHandlers>.Get();

private bool lastConditionNone = true;
private bool lastFramePvP = false;
private bool lastFramePvP;

[ServiceManager.ServiceConstructor]
private ClientState(SigScanner sigScanner, DalamudStartInfo startInfo, GameLifecycle lifecycle)
Expand All @@ -63,22 +64,22 @@ private ClientState(SigScanner sigScanner, DalamudStartInfo startInfo, GameLifec
private delegate IntPtr SetupTerritoryTypeDelegate(IntPtr manager, ushort terriType);

/// <inheritdoc/>
public event EventHandler<ushort> TerritoryChanged;
public event Action<ushort>? TerritoryChanged;

/// <inheritdoc/>
public event EventHandler Login;
public event Action? Login;

/// <inheritdoc/>
public event EventHandler Logout;
public event Action? Logout;

/// <inheritdoc/>
public event Action EnterPvP;
public event Action? EnterPvP;

/// <inheritdoc/>
public event Action LeavePvP;
public event Action? LeavePvP;

/// <inheritdoc/>
public event EventHandler<Lumina.Excel.GeneratedSheets.ContentFinderCondition> CfPop;
public event Action<ContentFinderCondition>? CfPop;

/// <inheritdoc/>
public ClientLanguage ClientLanguage { get; }
Expand Down Expand Up @@ -128,16 +129,16 @@ private void ContinueConstruction()
private IntPtr SetupTerritoryTypeDetour(IntPtr manager, ushort terriType)
{
this.TerritoryType = terriType;
this.TerritoryChanged?.InvokeSafely(this, terriType);
this.TerritoryChanged?.InvokeSafely(terriType);

Log.Debug("TerritoryType changed: {0}", terriType);

return this.setupTerritoryTypeHook.Original(manager, terriType);
}

private void NetworkHandlersOnCfPop(object sender, Lumina.Excel.GeneratedSheets.ContentFinderCondition e)
private void NetworkHandlersOnCfPop(ContentFinderCondition e)
{
this.CfPop?.InvokeSafely(this, e);
this.CfPop?.InvokeSafely(e);
}

private void FrameworkOnOnUpdateEvent(IFramework framework1)
Expand All @@ -149,12 +150,12 @@ private void FrameworkOnOnUpdateEvent(IFramework framework1)
if (condition == null || gameGui == null || data == null)
return;

if (condition.Any() && this.lastConditionNone == true && this.LocalPlayer != null)
if (condition.Any() && this.lastConditionNone && this.LocalPlayer != null)
{
Log.Debug("Is login");
this.lastConditionNone = false;
this.IsLoggedIn = true;
this.Login?.InvokeSafely(this, null);
this.Login?.InvokeSafely();
gameGui.ResetUiHideState();

this.lifecycle.ResetLogout();
Expand All @@ -165,7 +166,7 @@ private void FrameworkOnOnUpdateEvent(IFramework framework1)
Log.Debug("Is logout");
this.lastConditionNone = true;
this.IsLoggedIn = false;
this.Logout?.InvokeSafely(this, null);
this.Logout?.InvokeSafely();
gameGui.ResetUiHideState();

this.lifecycle.SetLogout();
Expand All @@ -189,3 +190,103 @@ private void FrameworkOnOnUpdateEvent(IFramework framework1)
}
}
}

/// <summary>
/// Plugin-scoped version of a GameConfig service.
/// </summary>
[PluginInterface]
[InterfaceVersion("1.0")]
[ServiceManager.ScopedService]
#pragma warning disable SA1015
[ResolveVia<IClientState>]
#pragma warning restore SA1015
internal class ClientStatePluginScoped : IDisposable, IServiceType, IClientState
{
[ServiceManager.ServiceDependency]
private readonly ClientState clientStateService = Service<ClientState>.Get();

/// <summary>
/// Initializes a new instance of the <see cref="ClientStatePluginScoped"/> class.
/// </summary>
internal ClientStatePluginScoped()
{
this.clientStateService.TerritoryChanged += this.TerritoryChangedForward;
this.clientStateService.Login += this.LoginForward;
this.clientStateService.Logout += this.LogoutForward;
this.clientStateService.EnterPvP += this.EnterPvPForward;
this.clientStateService.LeavePvP += this.ExitPvPForward;
this.clientStateService.CfPop += this.ContentFinderPopForward;
}

/// <inheritdoc/>
public event Action<ushort>? TerritoryChanged;

/// <inheritdoc/>
public event Action? Login;

/// <inheritdoc/>
public event Action? Logout;

/// <inheritdoc/>
public event Action? EnterPvP;

/// <inheritdoc/>
public event Action? LeavePvP;

/// <inheritdoc/>
public event Action<ContentFinderCondition>? CfPop;

/// <inheritdoc/>
public ClientLanguage ClientLanguage => this.clientStateService.ClientLanguage;

/// <inheritdoc/>
public ushort TerritoryType => this.clientStateService.TerritoryType;

/// <inheritdoc/>
public PlayerCharacter? LocalPlayer => this.clientStateService.LocalPlayer;

/// <inheritdoc/>
public ulong LocalContentId => this.clientStateService.LocalContentId;

/// <inheritdoc/>
public bool IsLoggedIn => this.clientStateService.IsLoggedIn;

/// <inheritdoc/>
public bool IsPvP => this.clientStateService.IsPvP;

/// <inheritdoc/>
public bool IsPvPExcludingDen => this.clientStateService.IsPvPExcludingDen;

/// <inheritdoc/>
public bool IsGPosing => this.clientStateService.IsGPosing;

/// <inheritdoc/>
public void Dispose()
{
this.clientStateService.TerritoryChanged -= this.TerritoryChangedForward;
this.clientStateService.Login -= this.LoginForward;
this.clientStateService.Logout -= this.LogoutForward;
this.clientStateService.EnterPvP -= this.EnterPvPForward;
this.clientStateService.LeavePvP -= this.ExitPvPForward;
this.clientStateService.CfPop -= this.ContentFinderPopForward;

this.TerritoryChanged = null;
this.Login = null;
this.Logout = null;
this.EnterPvP = null;
this.LeavePvP = null;
this.CfPop = null;
}

private void TerritoryChangedForward(ushort territoryId) => this.TerritoryChanged?.Invoke(territoryId);

private void LoginForward() => this.Login?.Invoke();

private void LogoutForward() => this.Logout?.Invoke();

private void EnterPvPForward() => this.EnterPvP?.Invoke();

private void ExitPvPForward() => this.LeavePvP?.Invoke();

private void ContentFinderPopForward(ContentFinderCondition cfc) => this.CfPop?.Invoke(cfc);
}
2 changes: 1 addition & 1 deletion Dalamud/Game/DutyState/DutyState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ private byte ContentDirectorNetworkMessageDetour(IntPtr a1, IntPtr a2, ushort* a
return this.contentDirectorNetworkMessageHook.Original(a1, a2, a3);
}

private void TerritoryOnChangedEvent(object? sender, ushort e)
private void TerritoryOnChangedEvent(ushort territoryId)
{
if (this.IsDutyStarted)
{
Expand Down
6 changes: 3 additions & 3 deletions Dalamud/Game/Network/Internal/NetworkHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ internal class NetworkHandlers : IDisposable, IServiceType
private NetworkHandlers(GameNetwork gameNetwork)
{
this.uploader = new UniversalisMarketBoardUploader();
this.CfPop = (_, _) => { };
this.CfPop = _ => { };

this.messages = Observable.Create<NetworkMessage>(observer =>
{
Expand Down Expand Up @@ -75,7 +75,7 @@ void Observe(IntPtr dataPtr, ushort opCode, uint sourceActorId, uint targetActor
/// <summary>
/// Event which gets fired when a duty is ready.
/// </summary>
public event EventHandler<ContentFinderCondition> CfPop;
public event Action<ContentFinderCondition> CfPop;

/// <summary>
/// Disposes of managed and unmanaged resources.
Expand Down Expand Up @@ -430,7 +430,7 @@ private unsafe IDisposable HandleCfPop()
Service<ChatGui>.GetNullable()?.Print($"Duty pop: {cfcName}");
}
this.CfPop.InvokeSafely(this, cfCondition);
this.CfPop.InvokeSafely(cfCondition);
}).ContinueWith(
task => Log.Error(task.Exception, "CfPop.Invoke failed"),
TaskContinuationOptions.OnlyOnFaulted);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ public void CleanUp()
this.subscribed = false;
}

private void ClientStateOnTerritoryChanged(object sender, ushort e)
private void ClientStateOnTerritoryChanged(ushort territoryId)
{
if (e == this.territory)
if (territoryId == this.territory)
{
this.hasPassed = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public void CleanUp()
}
}

private void ClientStateOnOnLogin(object sender, EventArgs e)
private void ClientStateOnOnLogin()
{
this.hasPassed = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public void CleanUp()
}
}

private void ClientStateOnOnLogout(object sender, EventArgs e)
private void ClientStateOnOnLogout()
{
this.hasPassed = true;
}
Expand Down
8 changes: 4 additions & 4 deletions Dalamud/Plugin/Services/IClientState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ public interface IClientState
/// <summary>
/// Event that gets fired when the current Territory changes.
/// </summary>
public event EventHandler<ushort> TerritoryChanged;
public event Action<ushort> TerritoryChanged;

/// <summary>
/// Event that fires when a character is logging in, and the local character object is available.
/// </summary>
public event EventHandler Login;
public event Action Login;

/// <summary>
/// Event that fires when a character is logging out.
/// </summary>
public event EventHandler Logout;
public event Action Logout;

/// <summary>
/// Event that fires when a character is entering PvP.
Expand All @@ -37,7 +37,7 @@ public interface IClientState
/// <summary>
/// Event that gets fired when a duty is ready.
/// </summary>
public event EventHandler<Lumina.Excel.GeneratedSheets.ContentFinderCondition> CfPop;
public event Action<Lumina.Excel.GeneratedSheets.ContentFinderCondition> CfPop;

/// <summary>
/// Gets the language of the client.
Expand Down
Loading

0 comments on commit b742abe

Please sign in to comment.