mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Move PlayerWatcher to own assembly and make appropriate changes for reuse.
This commit is contained in:
parent
ea40d5bc9c
commit
d99707f77e
9 changed files with 335 additions and 133 deletions
28
Penumbra.PlayerWatch/IPlayerWatcher.cs
Normal file
28
Penumbra.PlayerWatch/IPlayerWatcher.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using Dalamud.Game.ClientState.Actors.Types;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Penumbra.PlayerWatch
|
||||
{
|
||||
public delegate void ActorChange( Actor actor );
|
||||
|
||||
public interface IPlayerWatcherBase : IDisposable
|
||||
{
|
||||
public int Version { get; }
|
||||
public bool Valid { get; }
|
||||
}
|
||||
|
||||
public interface IPlayerWatcher : IPlayerWatcherBase
|
||||
{
|
||||
public event ActorChange? ActorChanged;
|
||||
public bool Active { get; }
|
||||
|
||||
public void Enable();
|
||||
public void Disable();
|
||||
public void SetStatus( bool enabled );
|
||||
|
||||
public void AddPlayerToWatch( string name );
|
||||
public void RemovePlayerFromWatch( string playerName );
|
||||
public CharEquipment UpdateActorWithoutEvent( Actor actor );
|
||||
}
|
||||
}
|
||||
41
Penumbra.PlayerWatch/Penumbra.PlayerWatch.csproj
Normal file
41
Penumbra.PlayerWatch/Penumbra.PlayerWatch.csproj
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<AssemblyTitle>Penumbra.PlayerWatch</AssemblyTitle>
|
||||
<Company>absolute gangstas</Company>
|
||||
<Product>Penumbra</Product>
|
||||
<Copyright>Copyright © 2020</Copyright>
|
||||
<FileVersion>1.0.0.0</FileVersion>
|
||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugType>full</DebugType>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);MSB3277</MSBuildWarningsAsMessages>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Dalamud">
|
||||
<HintPath>$(DALAMUD_ROOT)\Dalamud.dll</HintPath>
|
||||
<HintPath>..\libs\Dalamud.dll</HintPath>
|
||||
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Penumbra.GameData\Penumbra.GameData.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
164
Penumbra.PlayerWatch/PlayerWatchBase.cs
Normal file
164
Penumbra.PlayerWatch/PlayerWatchBase.cs
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Dalamud.Game.ClientState.Actors;
|
||||
using Dalamud.Game.ClientState.Actors.Types;
|
||||
using Dalamud.Plugin;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Penumbra.PlayerWatch
|
||||
{
|
||||
internal class PlayerWatchBase : IDisposable
|
||||
{
|
||||
public const int GPosePlayerActorIdx = 201;
|
||||
private const int ActorsPerFrame = 8;
|
||||
|
||||
private readonly DalamudPluginInterface _pi;
|
||||
internal readonly HashSet< PlayerWatcher > RegisteredWatchers = new();
|
||||
private readonly Dictionary< string, (CharEquipment, HashSet< PlayerWatcher >) > _equip = new();
|
||||
private int _frameTicker;
|
||||
private IntPtr _lastGPoseAddress = IntPtr.Zero;
|
||||
|
||||
internal PlayerWatchBase( DalamudPluginInterface pi )
|
||||
{
|
||||
_pi = pi;
|
||||
EnableActorWatch();
|
||||
}
|
||||
|
||||
internal void RegisterWatcher( PlayerWatcher watcher )
|
||||
{
|
||||
RegisteredWatchers.Add( watcher );
|
||||
}
|
||||
|
||||
internal void UnregisterWatcher( PlayerWatcher watcher )
|
||||
{
|
||||
if( RegisteredWatchers.Remove( watcher ) )
|
||||
{
|
||||
foreach( var items in _equip.Values )
|
||||
{
|
||||
items.Item2.Remove( watcher );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal CharEquipment UpdateActorWithoutEvent( Actor actor )
|
||||
{
|
||||
var equipment = new CharEquipment( actor );
|
||||
if( _equip.ContainsKey( actor.Name ) )
|
||||
{
|
||||
_equip[ actor.Name ] = ( equipment, _equip[ actor.Name ].Item2 );
|
||||
}
|
||||
|
||||
return equipment;
|
||||
}
|
||||
|
||||
internal void AddPlayerToWatch( string playerName, PlayerWatcher watcher )
|
||||
{
|
||||
if( _equip.TryGetValue( playerName, out var items ) )
|
||||
{
|
||||
items.Item2.Add( watcher );
|
||||
}
|
||||
else
|
||||
{
|
||||
_equip[ playerName ] = ( new CharEquipment(), new HashSet< PlayerWatcher > { watcher } );
|
||||
}
|
||||
}
|
||||
|
||||
public void RemovePlayerFromWatch( string playerName, PlayerWatcher watcher )
|
||||
{
|
||||
if( _equip.TryGetValue( playerName, out var items ) )
|
||||
{
|
||||
items.Item2.Remove( watcher );
|
||||
if( items.Item2.Count == 0 )
|
||||
{
|
||||
_equip.Remove( playerName );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void EnableActorWatch()
|
||||
{
|
||||
_pi.Framework.OnUpdateEvent += OnFrameworkUpdate;
|
||||
_pi.ClientState.TerritoryChanged += OnTerritoryChange;
|
||||
_pi.ClientState.OnLogout += OnLogout;
|
||||
}
|
||||
|
||||
internal void DisableActorWatch()
|
||||
{
|
||||
_pi.Framework.OnUpdateEvent -= OnFrameworkUpdate;
|
||||
_pi.ClientState.TerritoryChanged -= OnTerritoryChange;
|
||||
_pi.ClientState.OnLogout -= OnLogout;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
=> DisableActorWatch();
|
||||
|
||||
private void OnTerritoryChange( object _1, ushort _2 )
|
||||
=> Clear();
|
||||
|
||||
private void OnLogout( object _1, object _2 )
|
||||
=> Clear();
|
||||
|
||||
internal void Clear()
|
||||
{
|
||||
foreach( var kvp in _equip )
|
||||
{
|
||||
kvp.Value.Item1.Clear();
|
||||
}
|
||||
|
||||
_frameTicker = 0;
|
||||
}
|
||||
|
||||
private static void TriggerEvents( IEnumerable< PlayerWatcher > watchers, Actor actor )
|
||||
{
|
||||
foreach( var watcher in watchers.Where( w => w.Active ) )
|
||||
{
|
||||
watcher.Trigger( actor );
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFrameworkUpdate( object framework )
|
||||
{
|
||||
var actors = _pi.ClientState.Actors;
|
||||
var gPoseActor = actors[ GPosePlayerActorIdx ];
|
||||
if( gPoseActor == null )
|
||||
{
|
||||
if( _lastGPoseAddress != IntPtr.Zero && actors[ 0 ] != null && _equip.TryGetValue( actors[ 0 ].Name, out var player ) )
|
||||
{
|
||||
TriggerEvents( player.Item2, actors[ 0 ] );
|
||||
}
|
||||
|
||||
_lastGPoseAddress = IntPtr.Zero;
|
||||
}
|
||||
else if( gPoseActor.Address != _lastGPoseAddress )
|
||||
{
|
||||
_lastGPoseAddress = gPoseActor.Address;
|
||||
if( _equip.TryGetValue( gPoseActor.Name, out var gPose ) )
|
||||
{
|
||||
TriggerEvents( gPose.Item2, gPoseActor );
|
||||
}
|
||||
}
|
||||
|
||||
for( var i = 0; i < ActorsPerFrame; ++i )
|
||||
{
|
||||
_frameTicker = _frameTicker < actors.Length - 2
|
||||
? _frameTicker + 2
|
||||
: 0;
|
||||
|
||||
var actor = _frameTicker == 0 && gPoseActor != null ? gPoseActor : actors[ _frameTicker ];
|
||||
if( actor == null
|
||||
|| actor.ObjectKind != ObjectKind.Player
|
||||
|| actor.Name == null
|
||||
|| actor.Name.Length == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( _equip.TryGetValue( actor.Name, out var equip ) && !equip.Item1.CompareAndUpdate( actor ) )
|
||||
{
|
||||
TriggerEvents( equip.Item2, actor );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Penumbra.PlayerWatch/PlayerWatcher.cs
Normal file
88
Penumbra.PlayerWatch/PlayerWatcher.cs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
using System;
|
||||
using Dalamud.Game.ClientState.Actors.Types;
|
||||
using Dalamud.Plugin;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Penumbra.PlayerWatch
|
||||
{
|
||||
public class PlayerWatcher : IDisposable, IPlayerWatcher
|
||||
{
|
||||
public int Version { get; } = 1;
|
||||
|
||||
private static PlayerWatchBase? _playerWatch;
|
||||
|
||||
public event ActorChange? ActorChanged;
|
||||
|
||||
public bool Active { get; set; } = true;
|
||||
|
||||
public bool Valid
|
||||
=> _playerWatch != null;
|
||||
|
||||
internal PlayerWatcher( DalamudPluginInterface pi )
|
||||
{
|
||||
_playerWatch ??= new PlayerWatchBase( pi );
|
||||
_playerWatch.RegisterWatcher( this );
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
=> Active = Valid;
|
||||
|
||||
public void Disable()
|
||||
=> Active = false;
|
||||
|
||||
public void SetStatus( bool enabled )
|
||||
=> Active = enabled && Valid;
|
||||
|
||||
internal void Trigger( Actor actor )
|
||||
=> ActorChanged?.Invoke( actor );
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if( _playerWatch == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Active = false;
|
||||
ActorChanged = null;
|
||||
_playerWatch.UnregisterWatcher( this );
|
||||
if( _playerWatch.RegisteredWatchers.Count == 0 )
|
||||
{
|
||||
_playerWatch.Dispose();
|
||||
_playerWatch = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckValidity()
|
||||
{
|
||||
if( !Valid )
|
||||
{
|
||||
throw new Exception( $"PlayerWatch was already disposed." );
|
||||
}
|
||||
}
|
||||
|
||||
public void AddPlayerToWatch( string name )
|
||||
{
|
||||
CheckValidity();
|
||||
_playerWatch!.AddPlayerToWatch( name, this );
|
||||
}
|
||||
|
||||
public void RemovePlayerFromWatch( string playerName )
|
||||
{
|
||||
CheckValidity();
|
||||
_playerWatch!.RemovePlayerFromWatch( playerName, this );
|
||||
}
|
||||
|
||||
public CharEquipment UpdateActorWithoutEvent( Actor actor )
|
||||
{
|
||||
CheckValidity();
|
||||
return _playerWatch!.UpdateActorWithoutEvent( actor );
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlayerWatchFactory
|
||||
{
|
||||
public static IPlayerWatcher Create( DalamudPluginInterface pi )
|
||||
=> new PlayerWatcher( pi );
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue