GPose Together fixes

safeguard pose/world data generation loops

safeguard more

fix gpose together charas getting their collection nuked going outside gpose
This commit is contained in:
Stanley Dimant
2025-01-20 10:29:51 +01:00
committed by Loporrit
parent 93aff198f2
commit 833fbd0e25
3 changed files with 131 additions and 105 deletions

View File

@@ -29,7 +29,7 @@ public sealed class CharaDataCharacterHandler : DisposableMediatorSubscriberBase
{ {
foreach (var chara in _handledCharaData) foreach (var chara in _handledCharaData)
{ {
_ = RevertHandledChara(chara, reapplyPose: false); _ = RevertHandledChara(chara);
} }
}); });
@@ -56,11 +56,11 @@ public sealed class CharaDataCharacterHandler : DisposableMediatorSubscriberBase
base.Dispose(disposing); base.Dispose(disposing);
foreach (var chara in _handledCharaData) foreach (var chara in _handledCharaData)
{ {
_ = RevertHandledChara(chara, reapplyPose: false); _ = RevertHandledChara(chara);
} }
} }
public async Task RevertChara(string name, Guid? cPlusId, bool reapplyPose = true) public async Task RevertChara(string name, Guid? cPlusId)
{ {
Guid applicationId = Guid.NewGuid(); Guid applicationId = Guid.NewGuid();
await _ipcManager.Glamourer.RevertByNameAsync(Logger, name, applicationId).ConfigureAwait(false); await _ipcManager.Glamourer.RevertByNameAsync(Logger, name, applicationId).ConfigureAwait(false);
@@ -75,20 +75,20 @@ public sealed class CharaDataCharacterHandler : DisposableMediatorSubscriberBase
await _ipcManager.Penumbra.RedrawAsync(Logger, handler, applicationId, CancellationToken.None).ConfigureAwait(false); await _ipcManager.Penumbra.RedrawAsync(Logger, handler, applicationId, CancellationToken.None).ConfigureAwait(false);
} }
public async Task<bool> RevertHandledChara(string name, bool reapplyPose = true) public async Task<bool> RevertHandledChara(string name)
{ {
var handled = _handledCharaData.FirstOrDefault(f => string.Equals(f.Name, name, StringComparison.Ordinal)); var handled = _handledCharaData.FirstOrDefault(f => string.Equals(f.Name, name, StringComparison.Ordinal));
if (handled == null) return false; if (handled == null) return false;
_handledCharaData.Remove(handled); _handledCharaData.Remove(handled);
await _dalamudUtilService.RunOnFrameworkThread(() => RevertChara(handled.Name, handled.CustomizePlus, reapplyPose)).ConfigureAwait(false); await _dalamudUtilService.RunOnFrameworkThread(() => RevertChara(handled.Name, handled.CustomizePlus)).ConfigureAwait(false);
return true; return true;
} }
public Task RevertHandledChara(HandledCharaDataEntry? handled, bool reapplyPose = true) public Task RevertHandledChara(HandledCharaDataEntry? handled)
{ {
if (handled == null) return Task.CompletedTask; if (handled == null) return Task.CompletedTask;
_handledCharaData.Remove(handled); _handledCharaData.Remove(handled);
return _dalamudUtilService.RunOnFrameworkThread(() => RevertChara(handled.Name, handled.CustomizePlus, reapplyPose)); return _dalamudUtilService.RunOnFrameworkThread(() => RevertChara(handled.Name, handled.CustomizePlus));
} }
internal void AddHandledChara(HandledCharaDataEntry handledCharaDataEntry) internal void AddHandledChara(HandledCharaDataEntry handledCharaDataEntry)

View File

@@ -131,8 +131,7 @@ public class CharaDataGposeTogetherManager : DisposableMediatorSubscriberBase
_lastCreatedCharaData = (playerData, charaDataDownloadDto); _lastCreatedCharaData = (playerData, charaDataDownloadDto);
} }
_lastFullPoseData = null; ForceResendOwnData();
_lastWorldData = null;
if (_lastCreatedCharaData != null) if (_lastCreatedCharaData != null)
await _apiController.GposeLobbyPushCharacterData(_lastCreatedCharaData.Value.Dto) await _apiController.GposeLobbyPushCharacterData(_lastCreatedCharaData.Value.Dto)
@@ -251,8 +250,6 @@ public class CharaDataGposeTogetherManager : DisposableMediatorSubscriberBase
node["OffHand"]![bone.Key]!["Rotation"] = $"{bone.Value.RotationX.ToString(CultureInfo.InvariantCulture)}, {bone.Value.RotationY.ToString(CultureInfo.InvariantCulture)}, {bone.Value.RotationZ.ToString(CultureInfo.InvariantCulture)}, {bone.Value.RotationW.ToString(CultureInfo.InvariantCulture)}"; node["OffHand"]![bone.Key]!["Rotation"] = $"{bone.Value.RotationX.ToString(CultureInfo.InvariantCulture)}, {bone.Value.RotationY.ToString(CultureInfo.InvariantCulture)}, {bone.Value.RotationZ.ToString(CultureInfo.InvariantCulture)}, {bone.Value.RotationW.ToString(CultureInfo.InvariantCulture)}";
} }
Logger.LogTrace(node.ToJsonString(new System.Text.Json.JsonSerializerOptions() { WriteIndented = true }));
return node.ToJsonString(); return node.ToJsonString();
} }
@@ -370,6 +367,8 @@ public class CharaDataGposeTogetherManager : DisposableMediatorSubscriberBase
if (!_dalamudUtil.IsInGpose) continue; if (!_dalamudUtil.IsInGpose) continue;
if (_usersInLobby.Count == 0) continue; if (_usersInLobby.Count == 0) continue;
try
{
var chara = await _dalamudUtil.GetPlayerCharacterAsync().ConfigureAwait(false); var chara = await _dalamudUtil.GetPlayerCharacterAsync().ConfigureAwait(false);
if (_dalamudUtil.IsInGpose) if (_dalamudUtil.IsInGpose)
{ {
@@ -380,7 +379,10 @@ public class CharaDataGposeTogetherManager : DisposableMediatorSubscriberBase
var poseJson = await _brio.GetPoseAsync(chara.Address).ConfigureAwait(false); var poseJson = await _brio.GetPoseAsync(chara.Address).ConfigureAwait(false);
if (string.IsNullOrEmpty(poseJson)) continue; if (string.IsNullOrEmpty(poseJson)) continue;
var poseData = CreatePoseDataFromJson(poseJson, _poseGenerationExecutions++ >= 12 ? null : _lastFullPoseData); var lastFullData = _poseGenerationExecutions++ >= 12 ? null : _lastFullPoseData;
lastFullData = _forceResendFullPose ? _lastFullPoseData : lastFullData;
var poseData = CreatePoseDataFromJson(poseJson, lastFullData);
if (!poseData.IsDelta) if (!poseData.IsDelta)
{ {
_lastFullPoseData = poseData; _lastFullPoseData = poseData;
@@ -392,13 +394,21 @@ public class CharaDataGposeTogetherManager : DisposableMediatorSubscriberBase
(poseData.Bones.Keys.All(k => _lastDeltaPoseData.Value.Bones.ContainsKey(k) (poseData.Bones.Keys.All(k => _lastDeltaPoseData.Value.Bones.ContainsKey(k)
&& poseData.Bones.Values.All(k => _lastDeltaPoseData.Value.Bones.ContainsValue(k)))); && poseData.Bones.Values.All(k => _lastDeltaPoseData.Value.Bones.ContainsValue(k))));
if ((poseData.Bones.Any() || poseData.MainHand.Any() || poseData.OffHand.Any()) if (_forceResendFullPose || ((poseData.Bones.Any() || poseData.MainHand.Any() || poseData.OffHand.Any())
&& (!poseData.IsDelta || (poseData.IsDelta && !deltaIsSame))) && (!poseData.IsDelta || (poseData.IsDelta && !deltaIsSame))))
{
_forceResendFullPose = false;
await _apiController.GposeLobbyPushPoseData(poseData).ConfigureAwait(false); await _apiController.GposeLobbyPushPoseData(poseData).ConfigureAwait(false);
}
if (poseData.IsDelta) if (poseData.IsDelta)
_lastDeltaPoseData = poseData; _lastDeltaPoseData = poseData;
} }
catch (Exception ex)
{
Logger.LogWarning(ex, "Error during Pose Data Generation");
}
}
} }
private async Task GposeWorldPositionBackgroundTask(CancellationToken ct) private async Task GposeWorldPositionBackgroundTask(CancellationToken ct)
@@ -410,6 +420,8 @@ public class CharaDataGposeTogetherManager : DisposableMediatorSubscriberBase
// if there are no players in lobby, don't do anything // if there are no players in lobby, don't do anything
if (_usersInLobby.Count == 0) continue; if (_usersInLobby.Count == 0) continue;
try
{
// get own player data // get own player data
var player = (Dalamud.Game.ClientState.Objects.Types.ICharacter?)(await _dalamudUtil.GetPlayerCharacterAsync().ConfigureAwait(false)); var player = (Dalamud.Game.ClientState.Objects.Types.ICharacter?)(await _dalamudUtil.GetPlayerCharacterAsync().ConfigureAwait(false));
if (player == null) continue; if (player == null) continue;
@@ -441,8 +453,9 @@ public class CharaDataGposeTogetherManager : DisposableMediatorSubscriberBase
var loc = await _dalamudUtil.GetMapDataAsync().ConfigureAwait(false); var loc = await _dalamudUtil.GetMapDataAsync().ConfigureAwait(false);
worldData.LocationInfo = loc; worldData.LocationInfo = loc;
if (worldData != _lastWorldData) if (_forceResendWorldData || worldData != _lastWorldData)
{ {
_forceResendWorldData = false;
await _apiController.GposeLobbyPushWorldData(worldData).ConfigureAwait(false); await _apiController.GposeLobbyPushWorldData(worldData).ConfigureAwait(false);
_lastWorldData = worldData; _lastWorldData = worldData;
Logger.LogTrace("WorldData (gpose: {gpose}): {data}", _dalamudUtil.IsInGpose, worldData); Logger.LogTrace("WorldData (gpose: {gpose}): {data}", _dalamudUtil.IsInGpose, worldData);
@@ -450,7 +463,7 @@ public class CharaDataGposeTogetherManager : DisposableMediatorSubscriberBase
foreach (var entry in _usersInLobby) foreach (var entry in _usersInLobby)
{ {
if (!entry.Value.HasWorldDataUpdate || _dalamudUtil.IsInGpose) continue; if (!entry.Value.HasWorldDataUpdate || _dalamudUtil.IsInGpose || entry.Value.WorldData == null) continue;
var entryWorldData = entry.Value.WorldData!.Value; var entryWorldData = entry.Value.WorldData!.Value;
@@ -484,6 +497,11 @@ public class CharaDataGposeTogetherManager : DisposableMediatorSubscriberBase
} }
} }
} }
catch (Exception ex)
{
Logger.LogWarning(ex, "Error during World Data Generation");
}
}
} }
private void OnCutsceneFrameworkUpdate() private void OnCutsceneFrameworkUpdate()
@@ -523,6 +541,7 @@ public class CharaDataGposeTogetherManager : DisposableMediatorSubscriberBase
private void OnEnterGpose() private void OnEnterGpose()
{ {
ForceResendOwnData();
ResetOwnData(); ResetOwnData();
foreach (var data in _usersInLobby.Values) foreach (var data in _usersInLobby.Values)
{ {
@@ -533,6 +552,7 @@ public class CharaDataGposeTogetherManager : DisposableMediatorSubscriberBase
private void OnExitGpose() private void OnExitGpose()
{ {
ForceResendOwnData();
ResetOwnData(); ResetOwnData();
foreach (var data in _usersInLobby.Values) foreach (var data in _usersInLobby.Values)
{ {
@@ -540,10 +560,18 @@ public class CharaDataGposeTogetherManager : DisposableMediatorSubscriberBase
} }
} }
private bool _forceResendFullPose = false;
private bool _forceResendWorldData = false;
private void ForceResendOwnData()
{
_forceResendFullPose = true;
_forceResendWorldData = true;
}
private void ResetOwnData() private void ResetOwnData()
{ {
_lastFullPoseData = null;
_lastDeltaPoseData = null;
_poseGenerationExecutions = 0; _poseGenerationExecutions = 0;
_lastCreatedCharaData = null; _lastCreatedCharaData = null;
} }
@@ -654,8 +682,6 @@ public class CharaDataGposeTogetherManager : DisposableMediatorSubscriberBase
if (_usersInLobby.ContainsKey(userData.UID)) if (_usersInLobby.ContainsKey(userData.UID))
OnUserLeaveLobby(userData); OnUserLeaveLobby(userData);
_usersInLobby[userData.UID] = new(userData); _usersInLobby[userData.UID] = new(userData);
_lastFullPoseData = null;
_lastWorldData = null;
_ = PushCharacterDownloadDto(); _ = PushCharacterDownloadDto();
} }

View File

@@ -1013,7 +1013,7 @@ public sealed partial class CharaDataManager : DisposableMediatorSubscriberBase
if (string.IsNullOrEmpty(handledActor)) return; if (string.IsNullOrEmpty(handledActor)) return;
UiBlockingComputation = Task.Run(async () => UiBlockingComputation = Task.Run(async () =>
{ {
await _characterHandler.RevertHandledChara(handledActor, false).ConfigureAwait(false); await _characterHandler.RevertHandledChara(handledActor).ConfigureAwait(false);
var gposeChara = await _dalamudUtilService.GetGposeCharacterFromObjectTableByNameAsync(handledActor, true).ConfigureAwait(false); var gposeChara = await _dalamudUtilService.GetGposeCharacterFromObjectTableByNameAsync(handledActor, true).ConfigureAwait(false);
if (gposeChara != null) if (gposeChara != null)
await _ipcManager.Brio.DespawnActorAsync(gposeChara.Address).ConfigureAwait(false); await _ipcManager.Brio.DespawnActorAsync(gposeChara.Address).ConfigureAwait(false);