 b2276a1883
			
		
	
	b2276a1883
	
	
	
		
			
			* most of the groups refactoring on client * register OnMethods for group stuff * start implementing client (still pretty broken) * finish implementing new api first iteration * idk rework everything for pair shit (still WIP); goal is to remove PairedClients and GroupPairClients from ApiController * move everything to PairManager, remove dictionaries from APiController * remove admin stuff from client, cleanup * adjust reconnection handling, add new settings, todo still to remove access from old stuff that's marked obsolete from config * add back adding servers, fix intro ui * fix obsolete calls * adjust config namespace * add UI for setting animation/sound permissions to syncshells * add ConfigurationService to hot reload config on change from external * move transient data cache to configuration * add deleting service to ui * fix saving of transient resources * fix group pair user assignments * halt scanner when penumbra inactive, add visible/online/offline split to individual pairs and tags * add presence to syncshell ui * move fullpause from config to server config * fixes in code style * more codestyle * show info icon on player in shells, don't show icon when no changes from default state are made, add online notifs * fixes to intro UI --------- Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
		
			
				
	
	
		
			135 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			135 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using FFXIVClientStructs.FFXIV.Client.Game.Character;
 | |
| using System.Runtime.InteropServices;
 | |
| using MareSynchronos.Utils;
 | |
| using Penumbra.String;
 | |
| using MareSynchronos.API.Data.Enum;
 | |
| 
 | |
| namespace MareSynchronos.Models;
 | |
| 
 | |
| public class PlayerRelatedObject
 | |
| {
 | |
|     private readonly Func<IntPtr> getAddress;
 | |
| 
 | |
|     public unsafe Character* Character => (Character*)Address;
 | |
| 
 | |
|     private string _name;
 | |
| 
 | |
|     public ObjectKind ObjectKind { get; }
 | |
|     public IntPtr Address { get; set; }
 | |
|     public IntPtr DrawObjectAddress { get; set; }
 | |
| 
 | |
|     public IntPtr CurrentAddress
 | |
|     {
 | |
|         get
 | |
|         {
 | |
|             try
 | |
|             {
 | |
|                 return getAddress.Invoke();
 | |
|             }
 | |
|             catch
 | |
|             { return IntPtr.Zero; }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public PlayerRelatedObject(ObjectKind objectKind, IntPtr address, IntPtr drawObjectAddress, Func<IntPtr> getAddress)
 | |
|     {
 | |
|         ObjectKind = objectKind;
 | |
|         Address = address;
 | |
|         DrawObjectAddress = drawObjectAddress;
 | |
|         this.getAddress = getAddress;
 | |
|         _name = string.Empty;
 | |
|     }
 | |
| 
 | |
|     public byte[] EquipSlotData { get; set; } = new byte[40];
 | |
|     public byte[] CustomizeData { get; set; } = new byte[26];
 | |
|     public byte? HatState { get; set; }
 | |
|     public byte? VisorWeaponState { get; set; }
 | |
| 
 | |
|     public bool HasTransientsUpdate { get; set; } = false;
 | |
|     public bool HasUnprocessedUpdate { get; set; } = false;
 | |
|     public bool DoNotSendUpdate { get; set; } = false;
 | |
|     public bool IsProcessing { get; set; } = false;
 | |
| 
 | |
|     public unsafe void CheckAndUpdateObject()
 | |
|     {
 | |
|         var curPtr = CurrentAddress;
 | |
|         if (curPtr != IntPtr.Zero)
 | |
|         {
 | |
|             var chara = (Character*)curPtr;
 | |
|             bool addr = Address == IntPtr.Zero || Address != curPtr;
 | |
|             bool equip = CompareAndUpdateByteData(chara->EquipSlotData, chara->CustomizeData);
 | |
|             bool drawObj = (IntPtr)chara->GameObject.DrawObject != DrawObjectAddress;
 | |
|             var name = new ByteString(chara->GameObject.Name).ToString();
 | |
|             bool nameChange = (!string.Equals(name, _name, StringComparison.Ordinal));
 | |
|             if (addr || equip || drawObj || nameChange)
 | |
|             {
 | |
|                 _name = name;
 | |
|                 Logger.Verbose($"{ObjectKind} changed: {_name}, now: {curPtr:X}, {(IntPtr)chara->GameObject.DrawObject:X}");
 | |
| 
 | |
|                 Address = curPtr;
 | |
|                 DrawObjectAddress = (IntPtr)chara->GameObject.DrawObject;
 | |
|                 HasUnprocessedUpdate = true;
 | |
|             }
 | |
|         }
 | |
|         else if (Address != IntPtr.Zero || DrawObjectAddress != IntPtr.Zero)
 | |
|         {
 | |
|             Address = IntPtr.Zero;
 | |
|             DrawObjectAddress = IntPtr.Zero;
 | |
|             Logger.Verbose(ObjectKind + " Changed: " + _name + ", now: " + Address + ", " + DrawObjectAddress);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private unsafe bool CompareAndUpdateByteData(byte* equipSlotData, byte* customizeData)
 | |
|     {
 | |
|         bool hasChanges = false;
 | |
|         DoNotSendUpdate = false;
 | |
|         for (int i = 0; i < EquipSlotData.Length; i++)
 | |
|         {
 | |
|             var data = Marshal.ReadByte((IntPtr)equipSlotData, i);
 | |
|             if (EquipSlotData[i] != data)
 | |
|             {
 | |
|                 EquipSlotData[i] = data;
 | |
|                 hasChanges = true;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         for (int i = 0; i < CustomizeData.Length; i++)
 | |
|         {
 | |
|             var data = Marshal.ReadByte((IntPtr)customizeData, i);
 | |
|             if (CustomizeData[i] != data)
 | |
|             {
 | |
|                 CustomizeData[i] = data;
 | |
|                 hasChanges = true;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         var newHatState = Marshal.ReadByte((IntPtr)customizeData + 30, 0);
 | |
|         var newWeaponOrVisorState = Marshal.ReadByte((IntPtr)customizeData + 31, 0);
 | |
|         if (newHatState != HatState)
 | |
|         {
 | |
|             if (HatState != null && !hasChanges && !HasUnprocessedUpdate)
 | |
|             {
 | |
|                 Logger.Debug("Not Sending Update, only Hat changed");
 | |
|                 DoNotSendUpdate = true;
 | |
|             }
 | |
|             HatState = newHatState;
 | |
|             hasChanges = true;
 | |
|         }
 | |
| 
 | |
|         newWeaponOrVisorState &= 0b1101; // ignore drawing weapon
 | |
| 
 | |
|         if (newWeaponOrVisorState != VisorWeaponState)
 | |
|         {
 | |
|             if (VisorWeaponState != null && !hasChanges && !HasUnprocessedUpdate)
 | |
|             {
 | |
|                 Logger.Debug("Not Sending Update, only Visor/Weapon changed");
 | |
|                 DoNotSendUpdate = true;
 | |
|             }
 | |
|             VisorWeaponState = newWeaponOrVisorState;
 | |
|             hasChanges = true;
 | |
|         }
 | |
| 
 | |
|         return hasChanges;
 | |
|     }
 | |
| }
 |