Add keeping track of seen players to player watcher.

This commit is contained in:
Ottermandias 2021-11-16 16:03:49 +01:00
parent 906e057943
commit 743f83d12e
3 changed files with 340 additions and 333 deletions

View file

@ -3,18 +3,18 @@ using System.Collections.Generic;
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
namespace Penumbra.PlayerWatch namespace Penumbra.PlayerWatch;
{
public delegate void PlayerChange( Character actor );
public interface IPlayerWatcherBase : IDisposable public delegate void PlayerChange( Character actor );
{
public interface IPlayerWatcherBase : IDisposable
{
public int Version { get; } public int Version { get; }
public bool Valid { get; } public bool Valid { get; }
} }
public interface IPlayerWatcher : IPlayerWatcherBase public interface IPlayerWatcher : IPlayerWatcherBase
{ {
public event PlayerChange? PlayerChanged; public event PlayerChange? PlayerChanged;
public bool Active { get; } public bool Active { get; }
@ -26,6 +26,5 @@ namespace Penumbra.PlayerWatch
public void RemovePlayerFromWatch( string playerName ); public void RemovePlayerFromWatch( string playerName );
public CharacterEquipment UpdatePlayerWithoutEvent( Character actor ); public CharacterEquipment UpdatePlayerWithoutEvent( Character actor );
public IEnumerable< (string, (uint, CharacterEquipment)[]) > WatchedPlayers(); public IEnumerable< (string, (ulong, CharacterEquipment)[]) > WatchedPlayers();
}
} }

View file

@ -9,22 +9,22 @@ using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging; using Dalamud.Logging;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
namespace Penumbra.PlayerWatch namespace Penumbra.PlayerWatch;
internal readonly struct WatchedPlayer
{ {
internal readonly struct WatchedPlayer public readonly Dictionary< ulong, CharacterEquipment > FoundActors;
{
public readonly Dictionary< uint, CharacterEquipment > FoundActors;
public readonly HashSet< PlayerWatcher > RegisteredWatchers; public readonly HashSet< PlayerWatcher > RegisteredWatchers;
public WatchedPlayer( PlayerWatcher watcher ) public WatchedPlayer( PlayerWatcher watcher )
{ {
FoundActors = new Dictionary< uint, CharacterEquipment >(4); FoundActors = new Dictionary< ulong, CharacterEquipment >( 4 );
RegisteredWatchers = new HashSet< PlayerWatcher >{ watcher }; RegisteredWatchers = new HashSet< PlayerWatcher > { watcher };
}
} }
}
internal class PlayerWatchBase : IDisposable internal class PlayerWatchBase : IDisposable
{ {
public const int GPosePlayerIdx = 201; public const int GPosePlayerIdx = 201;
public const int GPoseTableEnd = GPosePlayerIdx + 48; public const int GPoseTableEnd = GPosePlayerIdx + 48;
private const int ObjectsPerFrame = 32; private const int ObjectsPerFrame = 32;
@ -34,6 +34,7 @@ namespace Penumbra.PlayerWatch
private readonly ObjectTable _objects; private readonly ObjectTable _objects;
internal readonly HashSet< PlayerWatcher > RegisteredWatchers = new(); internal readonly HashSet< PlayerWatcher > RegisteredWatchers = new();
internal readonly Dictionary< string, WatchedPlayer > Equip = new(); internal readonly Dictionary< string, WatchedPlayer > Equip = new();
internal HashSet< ulong > SeenActors;
private int _frameTicker; private int _frameTicker;
private bool _inGPose; private bool _inGPose;
private bool _enabled; private bool _enabled;
@ -44,6 +45,7 @@ namespace Penumbra.PlayerWatch
_framework = framework; _framework = framework;
_clientState = clientState; _clientState = clientState;
_objects = objects; _objects = objects;
SeenActors = new HashSet< ulong >( _objects.Length );
} }
internal void RegisterWatcher( PlayerWatcher watcher ) internal void RegisterWatcher( PlayerWatcher watcher )
@ -83,14 +85,14 @@ namespace Penumbra.PlayerWatch
} }
} }
private static uint GetId( GameObject actor ) private static ulong GetId( GameObject actor )
=> actor.ObjectId ^ actor.OwnerId; => actor.ObjectId | ( ( ulong )actor.OwnerId << 32 );
internal CharacterEquipment UpdatePlayerWithoutEvent( Character actor ) internal CharacterEquipment UpdatePlayerWithoutEvent( Character actor )
{ {
var name = actor.Name.ToString(); var name = actor.Name.ToString();
var equipment = new CharacterEquipment( actor ); var equipment = new CharacterEquipment( actor );
if (Equip.TryGetValue( name, out var watched )) if( Equip.TryGetValue( name, out var watched ) )
{ {
watched.FoundActors[ GetId( actor ) ] = equipment; watched.FoundActors[ GetId( actor ) ] = equipment;
} }
@ -242,6 +244,12 @@ namespace Penumbra.PlayerWatch
else if( _frameTicker == _objects.Length - 1 ) else if( _frameTicker == _objects.Length - 1 )
{ {
_frameTicker = 0; _frameTicker = 0;
foreach( var (_, equip) in Equip.Values.SelectMany( d => d.FoundActors.Where( p => !SeenActors.Contains( p.Key ) ) ) )
{
equip.Clear();
}
SeenActors.Clear();
} }
else else
{ {
@ -292,14 +300,15 @@ namespace Penumbra.PlayerWatch
} }
var id = GetId( character ); var id = GetId( character );
PluginLog.Verbose( "Comparing Gear for {PlayerName} ({Id}) at {Address}...", character.Name, id, character.Address); SeenActors.Add( id );
PluginLog.Verbose( "Comparing Gear for {PlayerName} ({Id}) at {Address}...", character.Name, id, character.Address );
if( !watch.FoundActors.TryGetValue( id, out var equip ) ) if( !watch.FoundActors.TryGetValue( id, out var equip ) )
{ {
equip = new CharacterEquipment( character ); equip = new CharacterEquipment( character );
watch.FoundActors[ id ] = equip; watch.FoundActors[ id ] = equip;
TriggerEvents( watch.RegisteredWatchers, character ); TriggerEvents( watch.RegisteredWatchers, character );
} }
else if (!equip.CompareAndUpdate( character )) else if( !equip.CompareAndUpdate( character ) )
{ {
TriggerEvents( watch.RegisteredWatchers, character ); TriggerEvents( watch.RegisteredWatchers, character );
} }
@ -307,5 +316,4 @@ namespace Penumbra.PlayerWatch
break; // Only one comparison per frame. break; // Only one comparison per frame.
} }
} }
}
} }

View file

@ -7,11 +7,12 @@ using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
namespace Penumbra.PlayerWatch namespace Penumbra.PlayerWatch;
public class PlayerWatcher : IPlayerWatcher
{ {
public class PlayerWatcher : IPlayerWatcher public int Version
{ => 3;
public int Version { get; } = 2;
private static PlayerWatchBase? _playerWatch; private static PlayerWatchBase? _playerWatch;
@ -86,18 +87,17 @@ namespace Penumbra.PlayerWatch
return _playerWatch!.UpdatePlayerWithoutEvent( actor ); return _playerWatch!.UpdatePlayerWithoutEvent( actor );
} }
public IEnumerable< (string, (uint, CharacterEquipment)[]) > WatchedPlayers() public IEnumerable< (string, (ulong, CharacterEquipment)[]) > WatchedPlayers()
{ {
CheckValidity(); CheckValidity();
return _playerWatch!.Equip return _playerWatch!.Equip
.Where( kvp => kvp.Value.RegisteredWatchers.Contains( this ) ) .Where( kvp => kvp.Value.RegisteredWatchers.Contains( this ) )
.Select( kvp => ( kvp.Key, kvp.Value.FoundActors.Select( kvp2 => ( kvp2.Key, kvp2.Value ) ).ToArray() ) ); .Select( kvp => ( kvp.Key, kvp.Value.FoundActors.Select( kvp2 => ( kvp2.Key, kvp2.Value ) ).ToArray() ) );
} }
} }
public static class PlayerWatchFactory public static class PlayerWatchFactory
{ {
public static IPlayerWatcher Create( Framework framework, ClientState clientState, ObjectTable objects ) public static IPlayerWatcher Create( Framework framework, ClientState clientState, ObjectTable objects )
=> new PlayerWatcher( framework, clientState, objects ); => new PlayerWatcher( framework, clientState, objects );
}
} }