mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Update PlayerWatcher to deal with multiple actors with the same name.
This commit is contained in:
parent
0b8a3d2d11
commit
b6304d43db
4 changed files with 75 additions and 37 deletions
|
|
@ -26,6 +26,6 @@ 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, CharacterEquipment) > WatchedPlayers();
|
public IEnumerable< (string, (uint, CharacterEquipment)[]) > WatchedPlayers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -11,21 +11,33 @@ using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
namespace Penumbra.PlayerWatch
|
namespace Penumbra.PlayerWatch
|
||||||
{
|
{
|
||||||
|
internal readonly struct WatchedPlayer
|
||||||
|
{
|
||||||
|
public readonly Dictionary< uint, CharacterEquipment > FoundActors;
|
||||||
|
public readonly HashSet< PlayerWatcher > RegisteredWatchers;
|
||||||
|
|
||||||
|
public WatchedPlayer( PlayerWatcher watcher )
|
||||||
|
{
|
||||||
|
FoundActors = new Dictionary< uint, CharacterEquipment >(4);
|
||||||
|
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;
|
||||||
|
|
||||||
private readonly Framework _framework;
|
private readonly Framework _framework;
|
||||||
private readonly ClientState _clientState;
|
private readonly ClientState _clientState;
|
||||||
private readonly ObjectTable _objects;
|
private readonly ObjectTable _objects;
|
||||||
internal readonly HashSet< PlayerWatcher > RegisteredWatchers = new();
|
internal readonly HashSet< PlayerWatcher > RegisteredWatchers = new();
|
||||||
internal readonly Dictionary< string, (CharacterEquipment, HashSet< PlayerWatcher >) > Equip = new();
|
internal readonly Dictionary< string, WatchedPlayer > Equip = new();
|
||||||
private int _frameTicker;
|
private int _frameTicker;
|
||||||
private bool _inGPose;
|
private bool _inGPose;
|
||||||
private bool _enabled;
|
private bool _enabled;
|
||||||
private bool _cancel;
|
private bool _cancel;
|
||||||
|
|
||||||
internal PlayerWatchBase( Framework framework, ClientState clientState, ObjectTable objects )
|
internal PlayerWatchBase( Framework framework, ClientState clientState, ObjectTable objects )
|
||||||
{
|
{
|
||||||
|
|
@ -47,9 +59,12 @@ namespace Penumbra.PlayerWatch
|
||||||
{
|
{
|
||||||
if( RegisteredWatchers.Remove( watcher ) )
|
if( RegisteredWatchers.Remove( watcher ) )
|
||||||
{
|
{
|
||||||
foreach( var items in Equip.Values )
|
foreach( var (key, value) in Equip.ToArray() )
|
||||||
{
|
{
|
||||||
items.Item2.Remove( watcher );
|
if( value.RegisteredWatchers.Remove( watcher ) && value.RegisteredWatchers.Count == 0 )
|
||||||
|
{
|
||||||
|
Equip.Remove( key );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,12 +83,16 @@ namespace Penumbra.PlayerWatch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static uint GetId( GameObject actor )
|
||||||
|
=> actor.ObjectId ^ actor.OwnerId;
|
||||||
|
|
||||||
internal CharacterEquipment UpdatePlayerWithoutEvent( Character actor )
|
internal CharacterEquipment UpdatePlayerWithoutEvent( Character actor )
|
||||||
{
|
{
|
||||||
|
var name = actor.Name.ToString();
|
||||||
var equipment = new CharacterEquipment( actor );
|
var equipment = new CharacterEquipment( actor );
|
||||||
if( Equip.ContainsKey( actor.Name.ToString() ) )
|
if (Equip.TryGetValue( name, out var watched ))
|
||||||
{
|
{
|
||||||
Equip[ actor.Name.ToString() ] = ( equipment, Equip[ actor.Name.ToString() ].Item2 );
|
watched.FoundActors[ GetId( actor ) ] = equipment;
|
||||||
}
|
}
|
||||||
|
|
||||||
return equipment;
|
return equipment;
|
||||||
|
|
@ -83,11 +102,11 @@ namespace Penumbra.PlayerWatch
|
||||||
{
|
{
|
||||||
if( Equip.TryGetValue( playerName, out var items ) )
|
if( Equip.TryGetValue( playerName, out var items ) )
|
||||||
{
|
{
|
||||||
items.Item2.Add( watcher );
|
items.RegisteredWatchers.Add( watcher );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Equip[ playerName ] = ( new CharacterEquipment(), new HashSet< PlayerWatcher > { watcher } );
|
Equip[ playerName ] = new WatchedPlayer( watcher );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,8 +114,7 @@ namespace Penumbra.PlayerWatch
|
||||||
{
|
{
|
||||||
if( Equip.TryGetValue( playerName, out var items ) )
|
if( Equip.TryGetValue( playerName, out var items ) )
|
||||||
{
|
{
|
||||||
items.Item2.Remove( watcher );
|
if( items.RegisteredWatchers.Remove( watcher ) && items.RegisteredWatchers.Count == 0 )
|
||||||
if( items.Item2.Count == 0 )
|
|
||||||
{
|
{
|
||||||
Equip.Remove( playerName );
|
Equip.Remove( playerName );
|
||||||
}
|
}
|
||||||
|
|
@ -140,7 +158,7 @@ namespace Penumbra.PlayerWatch
|
||||||
_cancel = true;
|
_cancel = true;
|
||||||
foreach( var kvp in Equip )
|
foreach( var kvp in Equip )
|
||||||
{
|
{
|
||||||
kvp.Value.Item1.Clear();
|
kvp.Value.FoundActors.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
_frameTicker = 0;
|
_frameTicker = 0;
|
||||||
|
|
@ -167,7 +185,7 @@ namespace Penumbra.PlayerWatch
|
||||||
|
|
||||||
if( Equip.TryGetValue( player.Name.ToString(), out var watcher ) )
|
if( Equip.TryGetValue( player.Name.ToString(), out var watcher ) )
|
||||||
{
|
{
|
||||||
TriggerEvents( watcher.Item2, ( Character )player );
|
TriggerEvents( watcher.RegisteredWatchers, ( Character )player );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -184,7 +202,7 @@ namespace Penumbra.PlayerWatch
|
||||||
var a = _objects[ i ];
|
var a = _objects[ i ];
|
||||||
if( a == null )
|
if( a == null )
|
||||||
{
|
{
|
||||||
return CharacterFactory.Convert( player);
|
return CharacterFactory.Convert( player );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( a.Name == player.Name )
|
if( a.Name == player.Name )
|
||||||
|
|
@ -193,14 +211,14 @@ namespace Penumbra.PlayerWatch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return CharacterFactory.Convert(player)!;
|
return CharacterFactory.Convert( player )!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetPlayer( GameObject gameObject, out (CharacterEquipment, HashSet< PlayerWatcher >) equip )
|
private bool TryGetPlayer( GameObject gameObject, out WatchedPlayer watch )
|
||||||
{
|
{
|
||||||
equip = default;
|
watch = default;
|
||||||
var name = gameObject.Name.ToString();
|
var name = gameObject.Name.ToString();
|
||||||
return name.Length != 0 && Equip.TryGetValue( name, out equip );
|
return name.Length != 0 && Equip.TryGetValue( name, out watch );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool InvalidObjectKind( ObjectKind kind )
|
private static bool InvalidObjectKind( ObjectKind kind )
|
||||||
|
|
@ -217,11 +235,17 @@ namespace Penumbra.PlayerWatch
|
||||||
private GameObject? GetNextObject()
|
private GameObject? GetNextObject()
|
||||||
{
|
{
|
||||||
if( _frameTicker == GPosePlayerIdx - 1 )
|
if( _frameTicker == GPosePlayerIdx - 1 )
|
||||||
|
{
|
||||||
_frameTicker = GPoseTableEnd;
|
_frameTicker = GPoseTableEnd;
|
||||||
|
}
|
||||||
else if( _frameTicker == _objects.Length - 1 )
|
else if( _frameTicker == _objects.Length - 1 )
|
||||||
|
{
|
||||||
_frameTicker = 0;
|
_frameTicker = 0;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
++_frameTicker;
|
++_frameTicker;
|
||||||
|
}
|
||||||
|
|
||||||
return _objects[ _frameTicker ];
|
return _objects[ _frameTicker ];
|
||||||
}
|
}
|
||||||
|
|
@ -247,9 +271,9 @@ namespace Penumbra.PlayerWatch
|
||||||
for( var i = 0; i < ObjectsPerFrame; ++i )
|
for( var i = 0; i < ObjectsPerFrame; ++i )
|
||||||
{
|
{
|
||||||
var actor = GetNextObject();
|
var actor = GetNextObject();
|
||||||
if( actor == null
|
if( actor == null
|
||||||
|| InvalidObjectKind(actor.ObjectKind)
|
|| InvalidObjectKind( actor.ObjectKind )
|
||||||
|| !TryGetPlayer( actor, out var equip ) )
|
|| !TryGetPlayer( actor, out var watch ) )
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -266,11 +290,20 @@ namespace Penumbra.PlayerWatch
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
PluginLog.Verbose( "Comparing Gear for {PlayerName} at {Address}...", character.Name, character.Address );
|
var id = GetId( character );
|
||||||
if( !equip.Item1.CompareAndUpdate( character ) )
|
PluginLog.Verbose( "Comparing Gear for {PlayerName} ({Id}) at {Address}...", character.Name, id, character.Address);
|
||||||
|
if( !watch.FoundActors.TryGetValue( id, out var equip ) )
|
||||||
{
|
{
|
||||||
TriggerEvents( equip.Item2, character );
|
equip = new CharacterEquipment( character );
|
||||||
|
watch.FoundActors[ id ] = equip;
|
||||||
|
TriggerEvents( watch.RegisteredWatchers, character );
|
||||||
}
|
}
|
||||||
|
else if (!equip.CompareAndUpdate( character ))
|
||||||
|
{
|
||||||
|
TriggerEvents( watch.RegisteredWatchers, character );
|
||||||
|
}
|
||||||
|
|
||||||
|
break; // Only one comparison per frame.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,12 +86,12 @@ namespace Penumbra.PlayerWatch
|
||||||
return _playerWatch!.UpdatePlayerWithoutEvent( actor );
|
return _playerWatch!.UpdatePlayerWithoutEvent( actor );
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable< (string, CharacterEquipment) > WatchedPlayers()
|
public IEnumerable< (string, (uint, CharacterEquipment)[]) > WatchedPlayers()
|
||||||
{
|
{
|
||||||
CheckValidity();
|
CheckValidity();
|
||||||
return _playerWatch!.Equip
|
return _playerWatch!.Equip
|
||||||
.Where( kvp => kvp.Value.Item2.Contains( this ) )
|
.Where( kvp => kvp.Value.RegisteredWatchers.Contains( this ) )
|
||||||
.Select( kvp => ( kvp.Key, kvp.Value.Item1 ) );
|
.Select( kvp => ( kvp.Key, kvp.Value.FoundActors.Select( kvp2 => ( kvp2.Key, kvp2.Value ) ).ToArray() ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using Dalamud.Game.ClientState.Objects.Types;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using Penumbra.Api;
|
using Penumbra.Api;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.GameData.Util;
|
using Penumbra.GameData.Util;
|
||||||
using Penumbra.Interop;
|
using Penumbra.Interop;
|
||||||
using Penumbra.Meta;
|
using Penumbra.Meta;
|
||||||
|
|
@ -28,13 +29,14 @@ namespace Penumbra.UI
|
||||||
}
|
}
|
||||||
|
|
||||||
var players = Penumbra.PlayerWatcher.WatchedPlayers().ToArray();
|
var players = Penumbra.PlayerWatcher.WatchedPlayers().ToArray();
|
||||||
if( !players.Any() )
|
var count = players.Sum( s => Math.Max(1, s.Item2.Length) );
|
||||||
|
if( count == 0 )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !ImGui.BeginTable( "##ObjectTable", 13, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.ScrollX,
|
if( !ImGui.BeginTable( "##ObjectTable", 13, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.ScrollX,
|
||||||
new Vector2( -1, ImGui.GetTextLineHeightWithSpacing() * 4 * players.Length ) ) )
|
new Vector2( -1, ImGui.GetTextLineHeightWithSpacing() * 4 * count ) ) )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -43,7 +45,10 @@ namespace Penumbra.UI
|
||||||
|
|
||||||
var identifier = GameData.GameData.GetIdentifier();
|
var identifier = GameData.GameData.GetIdentifier();
|
||||||
|
|
||||||
foreach( var (actor, equip) in players )
|
foreach( var (actor, equip) in players.SelectMany( kvp => kvp.Item2.Any()
|
||||||
|
? kvp.Item2
|
||||||
|
.Select( x => ( $"{kvp.Item1} ({x.Item1})", x.Item2 ) )
|
||||||
|
: new[] { ( kvp.Item1, new CharacterEquipment() ) } ) )
|
||||||
{
|
{
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue