From a86906f76d6fc7f119853057a9d94e40dbcaa512 Mon Sep 17 00:00:00 2001 From: Redmoonwow <50778349+Redmoonwow@users.noreply.github.com> Date: Sat, 5 Oct 2024 11:03:23 +0900 Subject: [PATCH] Optimize buffprocessor (#183) * [fix] Improvement BuffLog Output * [fix] Fixed Script errors * [Optimize] Optimize BuffProcessor --- Splatoon/Memory/BuffEffectProcessor.cs | 412 ++++++------------ Splatoon/Memory/DirectorUpdateProcessor.cs | 14 +- Splatoon/Splatoon.cs | 147 +++---- .../SplatoonScripting/ScriptingProcessor.cs | 195 +++++---- Splatoon/SplatoonScripting/SplatoonScript.cs | 17 +- Splatoon/Structures/RecordedStatus.cs | 2 +- SplatoonScripts/Tests/RedmoonTest1.cs | 85 ++++ 7 files changed, 423 insertions(+), 449 deletions(-) create mode 100644 SplatoonScripts/Tests/RedmoonTest1.cs diff --git a/Splatoon/Memory/BuffEffectProcessor.cs b/Splatoon/Memory/BuffEffectProcessor.cs index 9d37b690..57be518a 100644 --- a/Splatoon/Memory/BuffEffectProcessor.cs +++ b/Splatoon/Memory/BuffEffectProcessor.cs @@ -1,351 +1,227 @@ -using Dalamud.Game.ClientState.Objects.SubKinds; -using Dalamud.Game.ClientState.Statuses; -using ECommons.GameFunctions; -using ECommons.GameHelpers; -using ECommons.Hooks; +using ECommons.GameHelpers; +using FFXIVClientStructs.FFXIV.Client.Game; +using FFXIVClientStructs.FFXIV.Client.Game.Character; +using FFXIVClientStructs.FFXIV.Client.Game.Object; using Splatoon.SplatoonScripting; -using Splatoon.Structures; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace Splatoon.Memory; -internal class BuffEffectProcessor +internal unsafe class BuffEffectProcessor :IDisposable { #region types - private enum StatusChangeType + private enum StatusChangeType :byte { NoChange, Remove, Gain } - private enum StatusChangeResult + private enum StatusChangeResult :byte { NoChange, Change } + [StructLayout(LayoutKind.Explicit)] private struct CharacterStatusInfo { + [FieldOffset(0)] public uint ObjectID; - public RecordedStatus[] Statuses; - public int NoUpdateCount; - } - private record struct CharactorStatusDiffResult - { - public RecordedStatus StatusInfo; - public StatusChangeType ChangeType; + [FieldOffset(8)] + public Status* StatusPtr; } #endregion #region privateDefine - private Dictionary _charactorStatusInfos = []; - private static bool _isClearRequest = false; + private const int MAX_STATUS_NUM = 60; + // There are 629 object slots in total, but only 299 objects up to EventObject are needed. + private const int MAX_OBJECT_NUM = 299; + private const uint INVALID_OBJECTID = 0xE0000000; + + private static CharacterStatusInfo* _CharacterStatusInfoPtr = null; + private static bool _IsRunning = false; + #endregion + + #region DllLoad + [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] + private static extern void memset(void* dest, int c, int count); + [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] + private static extern void memcpy(void* dest, void* src, int count); #endregion #region public - public void ActorEffectUpdate() + public BuffEffectProcessor() { - if(_isClearRequest) - { - _charactorStatusInfos.Clear(); - _isClearRequest = false; - } - - // Increment the NoUpdateCount for all objects - IncrementNoUpdateCount(); - - // Loop through all objects in Svc.Objects - foreach(var gameObject in Svc.Objects) + try { - if(gameObject == null) - continue; - - // Check if it can be cast to IBattleChara - if(gameObject is IBattleChara battleChara) + _CharacterStatusInfoPtr = (CharacterStatusInfo*)Marshal.AllocHGlobal(sizeof(CharacterStatusInfo) * MAX_OBJECT_NUM); + memset(_CharacterStatusInfoPtr, 0, sizeof(CharacterStatusInfo) * MAX_OBJECT_NUM); + for(int i = 0; i < MAX_OBJECT_NUM; ++i) { - var objectID = battleChara.EntityId; - - // If the object exists, reset the counter - if(_charactorStatusInfos.TryGetValue(objectID, out var statusInfo)) - { - statusInfo.NoUpdateCount = 0; // Reset the counter as the object is confirmed to exist - _charactorStatusInfos[objectID] = statusInfo; - } - - var statuses = battleChara.StatusList; - - // Compare the current and previous status lists - CompareStatusList(objectID, statuses, out var changeStatuses); - - // Log the changes, including gameObject.Name - LogChanges(battleChara, changeStatuses); - - // Save the current status list - CopyStatusList(objectID, statuses); + _CharacterStatusInfoPtr[i].StatusPtr = (FFXIVClientStructs.FFXIV.Client.Game.Status*)Marshal.AllocHGlobal(sizeof(FFXIVClientStructs.FFXIV.Client.Game.Status) * MAX_STATUS_NUM); } - } - // Remove objects that have not been updated for 10 cycles - RemoveInactiveObjects(); - } - - public static void DirectorCheck(DirectorUpdateCategory category) - { - if(category == DirectorUpdateCategory.Commence || - category == DirectorUpdateCategory.Wipe) + _IsRunning = true; + } + catch(Exception ex) { - _isClearRequest = true; + DuoLog.Error($"[Splatoon]: {ex.Message}"); + _IsRunning = false; } } - #endregion - #region private - private void IncrementNoUpdateCount() + ~BuffEffectProcessor() { - // Increment the NoUpdateCount for all objects - foreach(var key in _charactorStatusInfos.Keys) - { - var statusInfo = _charactorStatusInfos[key]; - statusInfo.NoUpdateCount++; - _charactorStatusInfos[key] = statusInfo; - } + Dispose(false); } - private void RemoveInactiveObjects() + public void Dispose() { - // Add objects with NoUpdateCount >= 10 to the removal list - List toRemove = []; - foreach(var kvp in _charactorStatusInfos) - { - if(kvp.Value.NoUpdateCount >= 10) - { - toRemove.Add(kvp.Key); - } - } - - // Actually remove the objects - foreach(var objectID in toRemove) - { - _charactorStatusInfos.Remove(objectID); - } + Dispose(true); + GC.SuppressFinalize(this); } - private void CopyStatusList(uint objectID, StatusList statuses) + protected virtual void Dispose(bool disposing) { - var newStatusIds = GetStatusIds(statuses); - - if(_charactorStatusInfos.TryGetValue(objectID, out var existingInfo)) + for(int i = 0; i < MAX_OBJECT_NUM; ++i) { - if(!ArraysEqual(existingInfo.Statuses, newStatusIds)) + if(_CharacterStatusInfoPtr[i].StatusPtr != null) { - _charactorStatusInfos[objectID] = new CharacterStatusInfo - { - ObjectID = objectID, - Statuses = newStatusIds, - NoUpdateCount = 0 // Reset the counter as it has been updated - }; + Marshal.FreeHGlobal((IntPtr)_CharacterStatusInfoPtr[i].StatusPtr); + _CharacterStatusInfoPtr[i].StatusPtr = null; } } - else + if(_CharacterStatusInfoPtr != null) { - _charactorStatusInfos.Add(objectID, new CharacterStatusInfo - { - ObjectID = objectID, - Statuses = newStatusIds, - NoUpdateCount = 0 // Set the counter to 0 when adding a new object - }); + Marshal.FreeHGlobal((IntPtr)_CharacterStatusInfoPtr); + _CharacterStatusInfoPtr = null; } } - private StatusChangeResult CompareStatusList(uint objectID, StatusList statuses, out List changeStatuses) + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + public void ActorEffectUpdate() { - changeStatuses = []; - - if(!_charactorStatusInfos.TryGetValue(objectID, out var existingInfo)) + if(!_IsRunning) return; + try { - return StatusChangeResult.NoChange; + Update(); } - - var currentStatusIds = GetStatusIds(statuses); - - CheckGains(currentStatusIds, existingInfo.Statuses, changeStatuses); - CheckRemovals(currentStatusIds, existingInfo.Statuses, changeStatuses); - - return changeStatuses.Count > 0 ? StatusChangeResult.Change : StatusChangeResult.NoChange; - } - - private RecordedStatus[] GetStatusIds(StatusList statuses) - { - var statusIds = new RecordedStatus[statuses.Length]; - for(var i = 0; i < statuses.Length; i++) + catch(Exception ex) { - var status = statuses[i]; - statusIds[i] = status == null ? default : new RecordedStatus(status.GameData.Name.ToString(), status.StatusId, status.Param); + DuoLog.Error($"[Splatoon]: {ex.Message}"); + this.Dispose(); + _IsRunning = false; } - return statusIds; } - - private void CheckGains(RecordedStatus[] currentStatusIds, RecordedStatus[] oldStatusIds, List changeStatuses) + #endregion + private void Update() { - for(var i = 0; i < currentStatusIds.Length; i++) - { - if(System.Array.IndexOf(oldStatusIds, currentStatusIds[i]) < 0) + GameObjectManager* gameObjectManagerPtr = GameObjectManager.Instance(); + + for(int i = 0; i < MAX_OBJECT_NUM; ++i) + { + GameObject* gameObject = gameObjectManagerPtr->Objects.IndexSorted[i].Value; + if(gameObject == null) continue; + if(!gameObject->IsCharacter()) continue; + if(gameObject->EntityId == INVALID_OBJECTID) continue; + Character* character = (Character*)gameObject; + StatusManager* sm = (StatusManager*)character->GetStatusManager(); + Status* statusArray = (Status*)((byte*)sm + 0x08); + if(sm == null) continue; + if(statusArray == null) continue; + + // New object + if(_CharacterStatusInfoPtr[i].ObjectID != gameObject->EntityId) { - changeStatuses.Add(new CharactorStatusDiffResult - { - StatusInfo = currentStatusIds[i], - ChangeType = StatusChangeType.Gain - }); + memset(_CharacterStatusInfoPtr[i].StatusPtr, 0, sizeof(CharacterStatusInfo)); + _CharacterStatusInfoPtr[i].ObjectID = character->EntityId; + memcpy(&_CharacterStatusInfoPtr[i].StatusPtr[0], &statusArray[0], sizeof(FFXIVClientStructs.FFXIV.Client.Game.Status) * sm->NumValidStatuses); + continue; } - } - } - private void CheckRemovals(RecordedStatus[] currentStatusIds, RecordedStatus[] oldStatusIds, List changeStatuses) - { - for(var i = 0; i < oldStatusIds.Length; i++) - { - if(System.Array.IndexOf(currentStatusIds, oldStatusIds[i]) < 0) + // Existing object + // Check status change + Status status; + bool isChange = false; + for(int j = 0; j < sm->NumValidStatuses; ++j) { - changeStatuses.Add(new CharactorStatusDiffResult + if(_CharacterStatusInfoPtr[i].StatusPtr[j].StatusId != statusArray[j].StatusId) + { + if(_CharacterStatusInfoPtr[i].StatusPtr[j].StatusId == 0) + { + // Gain + status = statusArray[j]; + AddStatusLog(&statusArray[j], character); + ScriptingProcessor.OnGainBuffEffect(character->EntityId, status); + isChange = true; + } + else + { + // Remove + status = _CharacterStatusInfoPtr[i].StatusPtr[j]; + RemoveStatusLog(&_CharacterStatusInfoPtr[i].StatusPtr[j], character); + ScriptingProcessor.OnRemoveBuffEffect(character->EntityId, status); + + if(statusArray[j].StatusId != 0) + { + // Gain + status = statusArray[j]; + AddStatusLog(&statusArray[j], character); + ScriptingProcessor.OnGainBuffEffect(character->EntityId, status); + } + isChange = true; + } + } + + if(_CharacterStatusInfoPtr[i].StatusPtr[j].StackCount != statusArray[j].StackCount && !isChange) { - StatusInfo = oldStatusIds[i], - ChangeType = StatusChangeType.Remove - }); + // Update + status = statusArray[j]; + UpdateStatusLog(&statusArray[j], character); + ScriptingProcessor.OnUpdateBuffEffect(character->EntityId, status); + } } + + // Update status + memcpy(&_CharacterStatusInfoPtr[i].StatusPtr[0], &statusArray[0], sizeof(FFXIVClientStructs.FFXIV.Client.Game.Status) * sm->NumValidStatuses); } } - private bool ArraysEqual(RecordedStatus[] array1, RecordedStatus[] array2) - { - if(array1.Length != array2.Length) - return false; - for(var i = 0; i < array1.Length; i++) - { - if(array1[i] != array2[i]) - return false; - } - return true; - } + #region private + void AddStatusLog(Status* data, Character* gameObjectCharactor) => StatusLog("buff+", data, gameObjectCharactor); + void RemoveStatusLog(Status* data, Character* gameObjectCharactor) => StatusLog("buff-", data, gameObjectCharactor); + void UpdateStatusLog(Status* data, Character* gameObjectCharactor) => StatusLog("buff*", data, gameObjectCharactor); // Updated LogChanges method - private void LogChanges(IBattleChara battleChara, List changeStatuses) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void StatusLog(string prefix, Status* data, Character* gameObjectCharactor) { - List gainStatusInfos = []; - List removeRecordedStatusInfos = []; - var isPlayer = battleChara is IPlayerCharacter; - var pc = battleChara as IPlayerCharacter; - - foreach(var changeStatus in changeStatuses) + string text = ""; + string PositionString = P.Config.LogPosition == true ? $"({gameObjectCharactor->Position.ToString()})" : ""; + string ElementTrigger = ""; + if(gameObjectCharactor->ObjectKind == ObjectKind.Pc) { - switch(changeStatus.ChangeType) - { - case StatusChangeType.Gain: - gainStatusInfos.Add(changeStatus.StatusInfo); - break; - case StatusChangeType.Remove: - removeRecordedStatusInfos.Add(changeStatus.StatusInfo); - break; - } + ElementTrigger = $"[{prefix}]PC:{data->StatusId}:{gameObjectCharactor->ClassJob.ToString()}"; } - - if(gainStatusInfos.Count > 0) + else { - string text; - - if(P.Config.LogPosition) - { - foreach(var statusId in gainStatusInfos) - { - if(isPlayer && Svc.ClientState.LocalPlayer != null && Svc.ClientState.LocalPlayer.Address == pc.Address) - { - text = $"You ({battleChara.Position.ToString()}) gain the effect of {statusId.StatusName}({statusId.StatusId}) Param: {statusId.Param} ([buff+]You:{statusId}:{pc.GetJob().ToString()})"; - P.ChatMessageQueue.Enqueue(text); - } - - if(isPlayer) - { - text = $"{battleChara.Name} ({battleChara.Position.ToString()}) gains the effect of {statusId.StatusName}({statusId.StatusId}) Param: {statusId.Param} ([buff+]{ObjectFunctions.GetNameplateKind(pc).ToString()}:{statusId}:{pc.GetJob().ToString()})"; - } - else - { - text = $"{battleChara.Name} ({battleChara.Position.ToString()}) gains the effect of {statusId.StatusName}({statusId.StatusId}) Param: {statusId.Param} ([buff+]{battleChara.NameId}:{statusId})"; - } - P.ChatMessageQueue.Enqueue(text); - } - } - else - { - foreach(var statusId in gainStatusInfos) - { - if(isPlayer && Svc.ClientState.LocalPlayer != null && Svc.ClientState.LocalPlayer.Address == pc.Address) - { - text = $"You gain the effect of {statusId.StatusName}({statusId.StatusId}) Param: {statusId.Param} ([buff+]You:{statusId}:{pc.GetJob().ToString()})"; - P.ChatMessageQueue.Enqueue(text); - } - - if(isPlayer) - { - text = $"{battleChara.Name} gains the effect of {statusId.StatusName}({statusId.StatusId}) Param: {statusId.Param} ([buff+]{ObjectFunctions.GetNameplateKind(pc).ToString()}:{statusId}:{pc.GetJob().ToString()})"; - } - else - { - text = $"{battleChara.Name} gains the effect of {statusId.StatusName}({statusId.StatusId}) Param: {statusId.Param} ([buff+]{battleChara.NameId}:{statusId})"; - } - P.ChatMessageQueue.Enqueue(text); - } - } - ScriptingProcessor.OnGainBuffEffect(battleChara.EntityId, gainStatusInfos); + ElementTrigger = $"[{prefix}]{gameObjectCharactor->NameId}:{data->StatusId}"; } - if(removeRecordedStatusInfos.Count > 0) + if(gameObjectCharactor->EntityId == Svc.ClientState.LocalPlayer?.EntityId) { - string text; - if(P.Config.LogPosition) - { - foreach(var statusId in removeRecordedStatusInfos) - { - if(isPlayer && Svc.ClientState.LocalPlayer != null && Svc.ClientState.LocalPlayer.Address == pc.Address) - { - text = $"You ({battleChara.Position.ToString()}) loses the effect of {statusId.StatusName}({statusId.StatusId}) Param: {statusId.Param} ([buff-]You:{statusId}:{pc.GetJob().ToString()})"; - P.ChatMessageQueue.Enqueue(text); - } + text = $"You gains the effect of {data->StatusId} Param: {data->StackCount} ([{prefix}]You:{data->StatusId}:{Svc.ClientState.LocalPlayer.GetJob().ToString()})"; + P.ChatMessageQueue.Enqueue(text); + } - if(isPlayer) - { - text = $"{battleChara.Name} ({battleChara.Position.ToString()}) loses the effect of {statusId.StatusName}({statusId.StatusId}) Param: {statusId.Param} ([buff-]{ObjectFunctions.GetNameplateKind(pc).ToString()}:{statusId}:{pc.GetJob().ToString()})"; - } - else - { - text = $"{battleChara.Name} ({battleChara.Position.ToString()}) loses the effect of {statusId.StatusName}({statusId.StatusId}) Param: {statusId.Param} ([buff-]{battleChara.NameId}:{statusId})"; - } - P.ChatMessageQueue.Enqueue(text); - } - } - else - { - foreach(var statusId in removeRecordedStatusInfos) - { - if(isPlayer && Svc.ClientState.LocalPlayer != null && Svc.ClientState.LocalPlayer.Address == pc.Address) - { - text = $"You lose the effect of {statusId.StatusName}({statusId.StatusId}) Param: {statusId.Param} ([buff-]You:{statusId}:{pc.GetJob().ToString()})"; - P.ChatMessageQueue.Enqueue(text); - } + text = $"{gameObjectCharactor->NameString} ({PositionString}) gains the effect of {data->StatusId} Param: {data->StackCount} ({ElementTrigger})"; - if(isPlayer) - { - text = $"{battleChara.Name} loses the effect of {statusId.StatusName}({statusId.StatusId}) Param: {statusId.Param} ([buff-]{ObjectFunctions.GetNameplateKind(pc).ToString()}:{statusId}:{pc.GetJob().ToString()})"; - } - else - { - text = $"{battleChara.Name} loses the effect of {statusId.StatusName}({statusId.StatusId}) Param: {statusId.Param} ([buff-]{battleChara.NameId}:{statusId})"; - } - P.ChatMessageQueue.Enqueue(text); - } - } - ScriptingProcessor.OnRemoveBuffEffect(battleChara.EntityId, removeRecordedStatusInfos); - } + P.ChatMessageQueue.Enqueue(text); } #endregion } + diff --git a/Splatoon/Memory/DirectorUpdateProcessor.cs b/Splatoon/Memory/DirectorUpdateProcessor.cs index 742cb55f..47133666 100644 --- a/Splatoon/Memory/DirectorUpdateProcessor.cs +++ b/Splatoon/Memory/DirectorUpdateProcessor.cs @@ -1,15 +1,6 @@ -using Dalamud.Hooking; -using Dalamud.Memory; -using Dalamud.Utility.Signatures; -using ECommons.Hooks; -using Lumina.Data.Parsing.Tex.Buffers; +using ECommons.Hooks; using Splatoon.Modules; using Splatoon.SplatoonScripting; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Splatoon.Memory { @@ -17,13 +8,12 @@ internal unsafe static class DirectorUpdateProcessor { internal static void ProcessDirectorUpdate(long a1, long a2, DirectorUpdateCategory a3, uint a4, uint a5, int a6, int a7) { - if (P.Config.Logging) + if(P.Config.Logging) { var text = $"Director Update: {a3:X}, {a4:X8}, {a5:X8}, {a6:X8}, {a7:X8}"; Logger.Log(text); PluginLog.Verbose(text); } - BuffEffectProcessor.DirectorCheck(a3); ScriptingProcessor.OnDirectorUpdate(a3); } } diff --git a/Splatoon/Splatoon.cs b/Splatoon/Splatoon.cs index f374796b..c91b41af 100644 --- a/Splatoon/Splatoon.cs +++ b/Splatoon/Splatoon.cs @@ -30,7 +30,7 @@ using Localization = ECommons.LanguageHelpers.Localization; namespace Splatoon; -public unsafe class Splatoon : IDalamudPlugin +public unsafe class Splatoon :IDalamudPlugin { public const string DiscordURL = "https://discord.gg/Zzrcc8kmvy"; public string Name => "Splatoon"; @@ -87,7 +87,7 @@ public unsafe class Splatoon : IDalamudPlugin internal void Load(IDalamudPluginInterface pluginInterface) { - if (Loaded) + if(Loaded) { PluginLog.Fatal("Splatoon is already loaded, could not load again..."); return; @@ -119,12 +119,12 @@ internal void Load(IDalamudPluginInterface pluginInterface) Svc.ClientState.TerritoryChanged += TerritoryChangedEvent; Svc.PluginInterface.UiBuilder.DisableUserUiHide = Config.ShowOnUiHide; LimitGaugeResets = Svc.Data.GetExcelSheet().GetRow(2844).Text.ToString(); - foreach (var x in Svc.Data.GetExcelSheet(ClientLanguage.English) + foreach(var x in Svc.Data.GetExcelSheet(ClientLanguage.English) .Union(Svc.Data.GetExcelSheet(ClientLanguage.French)) .Union(Svc.Data.GetExcelSheet(ClientLanguage.Japanese)) .Union(Svc.Data.GetExcelSheet(ClientLanguage.German))) { - if (x.Singular != "") + if(x.Singular != "") { var n = x.Singular.ToString().ToLower(); NameNpcIDsAll[n] = x.RowId; @@ -132,13 +132,13 @@ internal void Load(IDalamudPluginInterface pluginInterface) } } var bNames = new HashSet(); - foreach (var lang in Enum.GetValues()) + foreach(var lang in Enum.GetValues()) { bNames.Clear(); - foreach (var x in Svc.Data.GetExcelSheet(lang)) + foreach(var x in Svc.Data.GetExcelSheet(lang)) { var n = x.Singular.ToString().ToLower(); - if (bNames.Contains(n)) + if(bNames.Contains(n)) { NameNpcIDs[n] = 0; PluginLog.Verbose($"Name npc id {n} is ambiguous"); @@ -201,7 +201,7 @@ public void Dispose() Svc.Commands.RemoveHandler("/loadsplatoon"); Svc.PluginInterface.UiBuilder.Draw -= loader.Draw; }); - if (!Loaded) + if(!Loaded) { P = null; return; @@ -226,6 +226,7 @@ public void Dispose() Safe(ObjectEffectProcessor.Dispose); Safe(AttachedInfo.Dispose); Safe(ScriptingProcessor.Dispose); + Safe(BuffEffectProcessor.Dispose); ECommonsMain.Dispose(); P = null; //Svc.Chat.Print("Disposing"); @@ -270,19 +271,19 @@ public void RemoveDynamicElements(string name) internal void OnChatMessage(XivChatType type, int timestamp, ref SeString sender, ref SeString message, ref bool isHandled) { var inttype = (int)type; - if (inttype == 2105 && LimitGaugeResets.Equals(message.ToString())) + if(inttype == 2105 && LimitGaugeResets.Equals(message.ToString())) { Phase++; CombatStarted = Environment.TickCount64; Svc.PluginInterface.UiBuilder.AddNotification($"Phase transition to Phase ??".Loc(Phase), this.Name, NotificationType.Info, 10000); } - if (!type.EqualsAny(ECommons.Constants.NormalChatTypes)) + if(!type.EqualsAny(ECommons.Constants.NormalChatTypes)) { var m = message.Payloads.Where(p => p is ITextProvider) .Cast() .Aggregate(new StringBuilder(), (sb, tp) => sb.Append(tp.Text.RemoveSymbols(InvalidSymbols).Replace("\n", " ")), sb => sb.ToString()); ChatMessageQueue.Enqueue(m); - if (P.Config.Logging && !((uint)type).EqualsAny(Utils.BlacklistedMessages)) + if(P.Config.Logging && !((uint)type).EqualsAny(Utils.BlacklistedMessages)) { Logger.Log($"[{type}] {m}"); } @@ -291,15 +292,15 @@ internal void OnChatMessage(XivChatType type, int timestamp, ref SeString sender internal void SetupShutdownHttp(bool enable) { - if (enable) + if(enable) { - if (HttpServer == null) + if(HttpServer == null) { try { HttpServer = new HTTPServer(this); } - catch (Exception e) + catch(Exception e) { Log("Critical error occurred while starting HTTP server.".Loc(), true); Log(e.Message, true); @@ -310,7 +311,7 @@ internal void SetupShutdownHttp(bool enable) } else { - if (HttpServer != null) + if(HttpServer != null) { HttpServer.Dispose(); HttpServer = null; @@ -321,27 +322,27 @@ internal void SetupShutdownHttp(bool enable) internal void TerritoryChangedEvent(ushort e) { Phase = 1; - if (SFind.Count > 0 && !P.Config.NoFindReset) + if(SFind.Count > 0 && !P.Config.NoFindReset) { SFind.Clear(); Notify.Info("Search stopped".Loc()); } - for (var i = dynamicElements.Count - 1; i >= 0; i--) + for(var i = dynamicElements.Count - 1; i >= 0; i--) { var de = dynamicElements[i]; - foreach (var l in de.Layouts) + foreach(var l in de.Layouts) { ResetLayout(l); } - foreach (var dt in de.DestroyTime) + foreach(var dt in de.DestroyTime) { - if (dt == (long)DestroyCondition.TERRITORY_CHANGE) + if(dt == (long)DestroyCondition.TERRITORY_CHANGE) { dynamicElements.RemoveAt(i); } } } - foreach (var l in Config.LayoutsL) + foreach(var l in Config.LayoutsL) { ResetLayout(l); } @@ -353,11 +354,11 @@ internal void TerritoryChangedEvent(ushort e) static void ResetLayout(Layout l) { - if (l.UseTriggers) + if(l.UseTriggers) { - foreach (var t in l.Triggers) + foreach(var t in l.Triggers) { - if (t.ResetOnTChange) + if(t.ResetOnTChange) { t.FiredState = 0; l.TriggerCondition = 0; @@ -367,7 +368,7 @@ static void ResetLayout(Layout l) } } } - if (l.Freezing && l.FreezeResetTerr) + if(l.Freezing && l.FreezeResetTerr) { l.FreezeInfo = new(); } @@ -381,48 +382,48 @@ internal void Tick(IFramework framework) PlaceholderCache.Clear(); LayoutAmount = 0; ElementAmount = 0; - if (LogObjects && Svc.ClientState.LocalPlayer != null) + if(LogObjects && Svc.ClientState.LocalPlayer != null) { - foreach (var t in Svc.Objects) + foreach(var t in Svc.Objects) { var ischar = t is ICharacter; var obj = (t.Name.ToString(), t.EntityId, (ulong)t.Struct()->GetGameObjectId(), t.DataId, ischar ? ((ICharacter)t).Struct()->CharacterData.ModelCharaId : 0, t.Struct()->GetNameId(), ischar ? ((ICharacter)t).NameId : 0, t.ObjectKind); loggedObjectList.TryAdd(obj, new ObjectInfo()); loggedObjectList[obj].ExistenceTicks++; loggedObjectList[obj].IsChar = ischar; - if (ischar) + if(ischar) { loggedObjectList[obj].Targetable = t.Struct()->GetIsTargetable(); loggedObjectList[obj].Visible = ((ICharacter)t).IsCharacterVisible(); - if (loggedObjectList[obj].Targetable) loggedObjectList[obj].TargetableTicks++; - if (loggedObjectList[obj].Visible) loggedObjectList[obj].VisibleTicks++; + if(loggedObjectList[obj].Targetable) loggedObjectList[obj].TargetableTicks++; + if(loggedObjectList[obj].Visible) loggedObjectList[obj].VisibleTicks++; } else { loggedObjectList[obj].Targetable = t.Struct()->GetIsTargetable(); - if (loggedObjectList[obj].Targetable) loggedObjectList[obj].TargetableTicks++; + if(loggedObjectList[obj].Targetable) loggedObjectList[obj].TargetableTicks++; } loggedObjectList[obj].Distance = Vector3.Distance(Svc.ClientState.LocalPlayer.Position, t.Position); loggedObjectList[obj].HitboxRadius = t.HitboxRadius; loggedObjectList[obj].Life = t.GetLifeTimeSeconds(); } } - while (tickScheduler.TryDequeue(out var action)) + while(tickScheduler.TryDequeue(out var action)) { action.Invoke(); } PlayerPosCache = null; S.RenderManager.ClearDisplayObjects(); - if (Svc.ClientState.LocalPlayer != null) + if(Svc.ClientState.LocalPlayer != null) { - if (ChatMessageQueue.Count > 5 * dequeueConcurrency) + if(ChatMessageQueue.Count > 5 * dequeueConcurrency) { dequeueConcurrency++; //InternalLog.Debug($"Too many queued messages ({ChatMessageQueue.Count}); concurrency increased to {dequeueConcurrency}"); } - for (var i = 0; i < dequeueConcurrency; i++) + for(var i = 0; i < dequeueConcurrency; i++) { - if (ChatMessageQueue.TryDequeue(out var ccm)) + if(ChatMessageQueue.TryDequeue(out var ccm)) { InternalLog.Verbose("Message: " + ccm); CurrentChatMessages.Add(ccm); @@ -435,15 +436,15 @@ internal void Tick(IFramework framework) } //if (CurrentChatMessages.Count > 0) PluginLog.Verbose($"Messages dequeued: {CurrentChatMessages.Count}"); var pl = Svc.ClientState.LocalPlayer; - if (Svc.ClientState.LocalPlayer.Address == nint.Zero) + if(Svc.ClientState.LocalPlayer.Address == nint.Zero) { Log("Pointer to LocalPlayer.Address is zero"); return; } - if (Svc.Condition[ConditionFlag.InCombat]) + if(Svc.Condition[ConditionFlag.InCombat]) { - if (CombatStarted == 0) + if(CombatStarted == 0) { CombatStarted = Environment.TickCount64; Log("Combat started event"); @@ -452,19 +453,19 @@ internal void Tick(IFramework framework) } else { - if (CombatStarted != 0) + if(CombatStarted != 0) { CombatStarted = 0; Log("Combat ended event"); ScriptingProcessor.OnCombatEnd(); AttachedInfo.VFXInfos.Clear(); - foreach (var l in Config.LayoutsL) + foreach(var l in Config.LayoutsL) { ResetLayout(l); } - foreach (var de in dynamicElements) + foreach(var de in dynamicElements) { - foreach (var l in de.Layouts) + foreach(var l in de.Layouts) { ResetLayout(l); } @@ -475,14 +476,14 @@ internal void Tick(IFramework framework) //if (CamAngleY > Config.maxcamY) return; - if (PinnedElementEditWindow.Script != null && PinnedElementEditWindow.EditingElement != null && !PinnedElementEditWindow.Script.InternalData.UnconditionalDraw) + if(PinnedElementEditWindow.Script != null && PinnedElementEditWindow.EditingElement != null && !PinnedElementEditWindow.Script.InternalData.UnconditionalDraw) { S.RenderManager.GetRenderer(PinnedElementEditWindow.EditingElement).ProcessElement(PinnedElementEditWindow.EditingElement, null, true); } - if (SFind.Count > 0) + if(SFind.Count > 0) { - foreach (var obj in SFind) + foreach(var obj in SFind) { var col = GradientColor.Get(Colors.Red.ToVector4(), Colors.Yellow.ToVector4(), 750); var findEl = new Element(1) @@ -507,48 +508,48 @@ internal void Tick(IFramework framework) ProcessS2W(); - foreach (var i in Config.LayoutsL) + foreach(var i in Config.LayoutsL) { ProcessLayout(i); } - ScriptingProcessor.Scripts.ForEach(x => { if (x.IsEnabled) x.Controller.Layouts.Values.Each(ProcessLayout); }); - ScriptingProcessor.Scripts.ForEach(x => { if (x.IsEnabled || x.InternalData.UnconditionalDraw) x.Controller.Elements.Each(z => S.RenderManager.GetRenderer(z.Value).ProcessElement(z.Value, null, x.InternalData.UnconditionalDraw && x.InternalData.UnconditionalDrawElements.Contains(z.Key))); }); - foreach (var e in InjectedElements) + ScriptingProcessor.Scripts.ForEach(x => { if(x.IsEnabled) x.Controller.Layouts.Values.Each(ProcessLayout); }); + ScriptingProcessor.Scripts.ForEach(x => { if(x.IsEnabled || x.InternalData.UnconditionalDraw) x.Controller.Elements.Each(z => S.RenderManager.GetRenderer(z.Value).ProcessElement(z.Value, null, x.InternalData.UnconditionalDraw && x.InternalData.UnconditionalDrawElements.Contains(z.Key))); }); + foreach(var e in InjectedElements) { S.RenderManager.GetRenderer(e).ProcessElement(e); //PluginLog.Information("Processing type " + e.type + JsonConvert.SerializeObject(e, Formatting.Indented)); } InjectedElements.Clear(); - for (var i = dynamicElements.Count - 1; i >= 0; i--) + for(var i = dynamicElements.Count - 1; i >= 0; i--) { var de = dynamicElements[i]; - foreach (var dt in de.DestroyTime) + foreach(var dt in de.DestroyTime) { - if (dt == (long)DestroyCondition.COMBAT_EXIT) + if(dt == (long)DestroyCondition.COMBAT_EXIT) { - if (!Svc.Condition[ConditionFlag.InCombat] && prevCombatState) + if(!Svc.Condition[ConditionFlag.InCombat] && prevCombatState) { dynamicElements.RemoveAt(i); continue; } } - else if (dt > 0) + else if(dt > 0) { - if (Environment.TickCount64 > dt) + if(Environment.TickCount64 > dt) { dynamicElements.RemoveAt(i); continue; } } } - foreach (var l in de.Layouts) + foreach(var l in de.Layouts) { ProcessLayout(l); } - foreach (var e in de.Elements) + foreach(var e in de.Elements) { S.RenderManager.GetRenderer(e).ProcessElement(e); } @@ -562,7 +563,7 @@ internal void Tick(IFramework framework) BuffEffectProcessor.ActorEffectUpdate(); ScriptingProcessor.OnUpdate(); } - catch (Exception e) + catch(Exception e) { Log("Caught exception: " + e.Message); Log(e.StackTrace); @@ -571,21 +572,21 @@ internal void Tick(IFramework framework) internal void ProcessLayout(Layout l) { - if (LayoutUtils.IsLayoutVisible(l)) + if(LayoutUtils.IsLayoutVisible(l)) { LayoutAmount++; - if (l.Freezing) + if(l.Freezing) { - if (l.FreezeInfo.CanDisplay()) + if(l.FreezeInfo.CanDisplay()) { S.RenderManager.StoreDisplayObjects(); - for (var i = 0; i < l.ElementsL.Count; i++) + for(var i = 0; i < l.ElementsL.Count; i++) { var element = l.ElementsL[i]; S.RenderManager.GetRenderer(element).ProcessElement(element, l); } var union = S.RenderManager.GetUnifiedDisplayObjects(); - if (union.Count > 0) + if(union.Count > 0) { l.FreezeInfo.States.Add(new() { @@ -600,23 +601,23 @@ internal void ProcessLayout(Layout l) } else { - for (var i = 0; i < l.ElementsL.Count; i++) + for(var i = 0; i < l.ElementsL.Count; i++) { var element = l.ElementsL[i]; S.RenderManager.GetRenderer(element).ProcessElement(element, l); } } } - for (var i = l.FreezeInfo.States.Count - 1; i >= 0; i--) + for(var i = l.FreezeInfo.States.Count - 1; i >= 0; i--) { var x = l.FreezeInfo.States[i]; - if (x.IsActive()) + if(x.IsActive()) { S.RenderManager.InjectDisplayObjects(x.Objects); } else { - if (x.IsExpired()) + if(x.IsExpired()) { l.FreezeInfo.States.RemoveAt(i); } @@ -633,20 +634,20 @@ public void BeginS2W(object cls, string x, string y, string z) internal void ProcessS2W() { - if (s2wInfo != null) + if(s2wInfo != null) { var lmbdown = Bitmask.IsBitSet(User32.GetKeyState(0x01), 15); var mousePos = ImGui.GetIO().MousePos; - if (Svc.GameGui.ScreenToWorld(new Vector2(mousePos.X, mousePos.Y), out var worldPos, Config.maxdistance * 5)) + if(Svc.GameGui.ScreenToWorld(new Vector2(mousePos.X, mousePos.Y), out var worldPos, Config.maxdistance * 5)) { s2wInfo.Apply(worldPos.X, worldPos.Z, worldPos.Y); } - if (!lmbdown && prevMouseState) + if(!lmbdown && prevMouseState) { s2wInfo = null; } prevMouseState = lmbdown; - if (Environment.TickCount64 % 500 < 250 && s2wInfo != null) + if(Environment.TickCount64 % 500 < 250 && s2wInfo != null) { var coords = s2wInfo.GetValues(); var x = coords.x; @@ -665,7 +666,7 @@ public void InjectElement(Element e) internal void Log(string s, bool tochat = false, ushort? chatColor = null) { - if (tochat) + if(tochat) { Svc.Chat.Print(s, messageTag: "Splatoon", tagColor: chatColor); } diff --git a/Splatoon/SplatoonScripting/ScriptingProcessor.cs b/Splatoon/SplatoonScripting/ScriptingProcessor.cs index 2c8e9a4e..be821dfe 100644 --- a/Splatoon/SplatoonScripting/ScriptingProcessor.cs +++ b/Splatoon/SplatoonScripting/ScriptingProcessor.cs @@ -1,8 +1,8 @@ using ECommons.Hooks; using ECommons.Hooks.ActionEffectTypes; using ECommons.LanguageHelpers; +using FFXIVClientStructs.FFXIV.Client.Game; using Splatoon.Gui.Scripting; -using Splatoon.Structures; using System.Collections.Immutable; using System.Security.Cryptography; using System.Text.RegularExpressions; @@ -33,7 +33,7 @@ internal static string ExtractNamespaceFromCode(string code) { var regex = NamespaceRegex(); var matches = regex.Match(code); - if (matches.Success && matches.Groups.Count > 1) + if(matches.Success && matches.Groups.Count > 1) { return matches.Groups[1].Value; } @@ -44,7 +44,7 @@ internal static string ExtractClassFromCode(string code) { var regex = ClassRegex(); var matches = regex.Match(code); - if (matches.Success && matches.Groups.Count > 1) + if(matches.Success && matches.Groups.Count > 1) { return matches.Groups[1].Value; } @@ -53,7 +53,7 @@ internal static string ExtractClassFromCode(string code) internal static void BlockingBeginUpdate(bool force = false) { - if (UpdateCompleted || force) + if(UpdateCompleted || force) { Blacklist = ImmutableList.Empty; @@ -65,10 +65,10 @@ internal static void BlockingBeginUpdate(bool force = false) PluginLog.Debug($"Blacklist download complete"); var blacklist = result.Content.ReadAsStringAsync().Result; - foreach (var line in blacklist.Replace("\r", "").Split("\n")) + foreach(var line in blacklist.Replace("\r", "").Split("\n")) { var data = line.Split(","); - if (data.Length == 2 && int.TryParse(data[1], out var ver)) + if(data.Length == 2 && int.TryParse(data[1], out var ver)) { Blacklist = Blacklist.Add(new(data[0], ver)); PluginLog.Debug($"Found new valid blacklist data: {data[0]} v{ver}"); @@ -79,7 +79,7 @@ internal static void BlockingBeginUpdate(bool force = false) } } } - catch (Exception e) + catch(Exception e) { e.Log(); } @@ -87,10 +87,10 @@ internal static void BlockingBeginUpdate(bool force = false) Svc.Framework.RunOnFrameworkThread(delegate { PluginLog.Information($"Blacklist: {Blacklist.Select(x => $"{x.FullName} v{x.Version}").Print()}"); - foreach (var x in Scripts) + foreach(var x in Scripts) { x.InternalData.Allowed = true; - if (Blacklist.Any(z => z.FullName == x.InternalData.FullName && z.Version >= (x.Metadata?.Version ?? 0))) + if(Blacklist.Any(z => z.FullName == x.InternalData.FullName && z.Version >= (x.Metadata?.Version ?? 0))) { PluginLog.Information($"Script {x.InternalData.FullName} is blacklisted and will not be enabled"); x.InternalData.Blacklisted = true; @@ -110,13 +110,13 @@ internal static void BlockingBeginUpdate(bool force = false) var updateList = result.Content.ReadAsStringAsync().Result; List Updates = new(); - foreach (var line in updateList.Replace("\r", "").Split("\n")) + foreach(var line in updateList.Replace("\r", "").Split("\n")) { var data = line.Split(","); - if (data.Length >= 3 && int.TryParse(data[1], out var ver)) + if(data.Length >= 3 && int.TryParse(data[1], out var ver)) { PluginLog.Debug($"Found new valid update data: {data[0]} v{ver} = {data[2]}"); - if ((ForceUpdate != null && ForceUpdate.Contains(data[0])) || Scripts.Any(x => x.InternalData.FullName == data[0] && ((x.Metadata?.Version ?? 0) < ver || TabScripting.ForceUpdate))) // possible CME + if((ForceUpdate != null && ForceUpdate.Contains(data[0])) || Scripts.Any(x => x.InternalData.FullName == data[0] && ((x.Metadata?.Version ?? 0) < ver || TabScripting.ForceUpdate))) // possible CME { PluginLog.Debug($"Adding {data[2]} to download list"); Updates.Add(new(data[2])); @@ -128,13 +128,13 @@ internal static void BlockingBeginUpdate(bool force = false) } } ForceUpdate = null; - foreach (var x in Updates) + foreach(var x in Updates) { PluginLog.Information($"Downloading script from {x}"); BlockingDownloadScript(x); } } - catch (Exception e) + catch(Exception e) { e.Log(); } @@ -167,7 +167,7 @@ static void BlockingDownloadScript(string url) var result = P.HttpClient.GetStringAsync(url).Result; CompileAndLoad(result, null); } - catch (Exception e) + catch(Exception e) { e.Log(); } @@ -175,7 +175,7 @@ static void BlockingDownloadScript(string url) internal static void ReloadAll() { - if (ThreadIsRunning) + if(ThreadIsRunning) { DuoLog.Error("Can not reload yet, please wait"); return; @@ -184,11 +184,11 @@ internal static void ReloadAll() Scripts.ForEach(x => x.Disable()); Scripts = ImmutableList.Empty; var dir = Path.Combine(Svc.PluginInterface.GetPluginConfigDirectory(), "Scripts"); - if (!Directory.Exists(dir)) + if(!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } - foreach (var f in Directory.GetFiles(dir, "*.cs", SearchOption.AllDirectories)) + foreach(var f in Directory.GetFiles(dir, "*.cs", SearchOption.AllDirectories)) { CompileAndLoad(File.ReadAllText(f, Encoding.UTF8), f); } @@ -196,7 +196,7 @@ internal static void ReloadAll() internal static void ReloadScript(SplatoonScript s) { - if (ThreadIsRunning) + if(ThreadIsRunning) { DuoLog.Error("Can not reload yet, please wait"); return; @@ -210,7 +210,7 @@ internal static void CompileAndLoad(string sourceCode, string fpath) { PluginLog.Debug($"Requested script loading"); LoadScriptQueue.Enqueue((sourceCode, fpath)); - if (!ThreadIsRunning) + if(!ThreadIsRunning) { ThreadIsRunning = true; PluginLog.Debug($"Beginning new thread"); @@ -221,23 +221,23 @@ internal static void CompileAndLoad(string sourceCode, string fpath) PluginLog.Debug($"Compiler thread started"); int idleCount = 0; var dir = Path.Combine(Svc.PluginInterface.GetPluginConfigDirectory(), "ScriptCache"); - if (!Directory.Exists(dir)) + if(!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } - while (idleCount < 10) + while(idleCount < 10) { - if (LoadScriptQueue.TryDequeue(out var result)) + if(LoadScriptQueue.TryDequeue(out var result)) { try { byte[] code = null; - if (!P.Config.DisableScriptCache) + if(!P.Config.DisableScriptCache) { var md5 = MD5.HashData(Encoding.UTF8.GetBytes(result.code)).Select(x => $"{x:X2}").Join(""); var cacheFile = Path.Combine(dir, $"{md5}-{P.loader.splatoonVersion}.bin"); PluginLog.Debug($"Cache path: {cacheFile}"); - if (File.Exists(cacheFile)) + if(File.Exists(cacheFile)) { PluginLog.Debug($"Loading from cache..."); code = File.ReadAllBytes(cacheFile); @@ -246,7 +246,7 @@ internal static void CompileAndLoad(string sourceCode, string fpath) { PluginLog.Debug($"Compiling..."); code = Compiler.Compile(result.code, result.path == null ? "" : Path.GetFileNameWithoutExtension(result.path)); - if (code != null) + if(code != null) { File.WriteAllBytes(cacheFile, code); PluginLog.Debug($"Compiled and saved"); @@ -258,23 +258,23 @@ internal static void CompileAndLoad(string sourceCode, string fpath) PluginLog.Debug($"Compiling, cache bypassed..."); code = Compiler.Compile(result.code, result.path == null ? "" : Path.GetFileNameWithoutExtension(result.path)); } - if (code != null) + if(code != null) { Svc.Framework.RunOnFrameworkThread(delegate { - if (P != null && !P.Disposed) + if(P != null && !P.Disposed) { var assembly = Compiler.Load(code); - foreach (var t in assembly.GetTypes()) + foreach(var t in assembly.GetTypes()) { - if (t.BaseType?.FullName == "Splatoon.SplatoonScripting.SplatoonScript") + if(t.BaseType?.FullName == "Splatoon.SplatoonScripting.SplatoonScript") { var instance = (SplatoonScript)assembly.CreateInstance(t.FullName); instance.InternalData = new(result.path, instance); instance.InternalData.Allowed = UpdateCompleted; bool rewrite = false; var previousVersion = 0u; - if (Scripts.TryGetFirst(z => z.InternalData.FullName == instance.InternalData.FullName, out var loadedScript)) + if(Scripts.TryGetFirst(z => z.InternalData.FullName == instance.InternalData.FullName, out var loadedScript)) { DuoLog.Information($"Script {instance.InternalData.FullName} already loaded, replacing."); previousVersion = loadedScript.Metadata?.Version ?? 0; @@ -284,10 +284,10 @@ internal static void CompileAndLoad(string sourceCode, string fpath) rewrite = true; } Scripts = Scripts.Add(instance); - if (result.path == null) + if(result.path == null) { var dir = Path.Combine(Svc.PluginInterface.GetPluginConfigDirectory(), "Scripts", instance.InternalData.Namespace); - if (!Directory.Exists(dir)) + if(!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } @@ -296,7 +296,7 @@ internal static void CompileAndLoad(string sourceCode, string fpath) File.WriteAllText(newPath, result.code, Encoding.UTF8); DuoLog.Debug($"Script installed to {newPath}"); } - else if (rewrite) + else if(rewrite) { //DeleteFileToRecycleBin(result.path); File.WriteAllText(result.path, result.code, Encoding.UTF8); @@ -326,7 +326,7 @@ internal static void CompileAndLoad(string sourceCode, string fpath) PluginLog.Error("Loading process ended with error"); } } - catch (Exception e) + catch(Exception e) { e.Log(); } @@ -340,21 +340,21 @@ internal static void CompileAndLoad(string sourceCode, string fpath) } } } - catch (Exception e) + catch(Exception e) { e.Log(); } ThreadIsRunning = false; PluginLog.Debug($"Compiler part of thread is finished"); - if (!UpdateCompleted) + if(!UpdateCompleted) { PluginLog.Debug($"Starting updating..."); try { BlockingBeginUpdate(true); } - catch (Exception e) + catch(Exception e) { e.Log(); } @@ -368,9 +368,9 @@ internal static void CompileAndLoad(string sourceCode, string fpath) internal static void OnUpdate() { var tickCount = Environment.TickCount64; - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { var script = Scripts[i]; try @@ -389,9 +389,9 @@ internal static void OnUpdate() internal static void OnCombatStart() { - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { OnReset(i); try @@ -405,16 +405,16 @@ internal static void OnCombatStart() internal static void OnCombatEnd() { - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { OnReset(i); try { Scripts[i].OnCombatEnd(); } - catch (Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnCombatEnd)); } + catch(Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnCombatEnd)); } } } } @@ -435,114 +435,114 @@ internal static void OnReset(SplatoonScript script) internal static void OnMapEffect(uint Position, ushort Param1, ushort Param2) { - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { try { Scripts[i].OnMapEffect(Position, Param1, Param2); } - catch (Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnMapEffect)); } + catch(Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnMapEffect)); } } } } internal static void OnObjectEffect(uint Target, ushort Param1, ushort Param2) { - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { try { Scripts[i].OnObjectEffect(Target, Param1, Param2); } - catch (Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnObjectEffect)); } + catch(Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnObjectEffect)); } } } } internal static void OnStartingCast(uint source, uint castId) { - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { try { Scripts[i].OnStartingCast(source, castId); } - catch (Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnObjectEffect)); } + catch(Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnObjectEffect)); } } } } internal static void OnMessage(string Message) { - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { try { Scripts[i].OnMessage(Message); } - catch (Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnMessage)); } + catch(Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnMessage)); } } } } internal static void OnVFXSpawn(uint target, string vfxPath) { - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { try { Scripts[i].OnVFXSpawn(target, vfxPath); } - catch (Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnVFXSpawn)); } + catch(Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnVFXSpawn)); } } } } internal static void OnTetherCreate(uint source, uint target, uint data2, uint data3, uint data5) { - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { try { Scripts[i].OnTetherCreate(source, target, data2, data3, data5); } - catch (Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnTetherCreate)); } + catch(Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnTetherCreate)); } } } } internal static void OnTetherRemoval(uint source, uint data2, uint data3, uint data5) { - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { try { Scripts[i].OnTetherRemoval(source, data2, data3, data5); } - catch (Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnTetherRemoval)); } + catch(Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnTetherRemoval)); } } } } internal static void OnDirectorUpdate(DirectorUpdateCategory category) { - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { if (category == DirectorUpdateCategory.Commence || category == DirectorUpdateCategory.Recommence || category == DirectorUpdateCategory.Wipe) { @@ -552,52 +552,52 @@ internal static void OnDirectorUpdate(DirectorUpdateCategory category) { Scripts[i].OnDirectorUpdate(category); } - catch (Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnDirectorUpdate)); } + catch(Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnDirectorUpdate)); } } } } internal static void OnPhaseChange(int phase) { - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { try { Scripts[i].OnPhaseChange(phase); } - catch (Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnPhaseChange)); } + catch(Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnPhaseChange)); } } } } internal static void OnObjectCreation(nint newObjectPointer) { - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { try { Scripts[i].OnObjectCreation(newObjectPointer); } - catch (Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnObjectCreation)); } + catch(Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnObjectCreation)); } } } } internal static void OnActionEffect(uint ActionID, ushort animationID, ActionEffectType type, uint sourceID, ulong targetOID, uint damage) { - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { try { Scripts[i].OnActionEffect(ActionID, animationID, type, sourceID, targetOID, damage); } - catch (Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnActionEffect)); } + catch(Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnActionEffect)); } } } } @@ -619,20 +619,20 @@ internal static void OnActorControl(uint sourceId, uint command, uint p1, uint p internal static void OnActionEffectEvent(ActionEffectSet set) { - for (var i = 0; i < Scripts.Count; i++) + for(var i = 0; i < Scripts.Count; i++) { - if (Scripts[i].IsEnabled) + if(Scripts[i].IsEnabled) { try { Scripts[i].OnActionEffectEvent(set); } - catch (Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnActionEffectEvent)); } + catch(Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnActionEffectEvent)); } } } } - internal static void OnGainBuffEffect(uint sourceId, IReadOnlyList gainStatusInfos) + internal static void OnGainBuffEffect(uint sourceId, Status Status) { for (var i = 0; i < Scripts.Count; i++) { @@ -640,14 +640,29 @@ internal static void OnGainBuffEffect(uint sourceId, IReadOnlyList removeStatusInfos) + internal static void OnRemoveBuffEffect(uint sourceId, Status Status) + { + for(var i = 0; i < Scripts.Count; i++) + { + if(Scripts[i].IsEnabled) + { + try + { + Scripts[i].OnRemoveBuffEffect(sourceId, Status); + } + catch(Exception e) { Scripts[i].LogError(e, nameof(SplatoonScript.OnRemoveBuffEffect)); } + } + } + } + + internal static void OnUpdateBuffEffect(uint sourceId, Status status) { for (var i = 0; i < Scripts.Count; i++) { @@ -655,7 +670,7 @@ internal static void OnRemoveBuffEffect(uint sourceId, IReadOnlyList /// Source object ID of buff gain. - /// Array of gained buff Infos. - public virtual void OnGainBuffEffect(uint sourceId, IReadOnlyList gainStatusInfos) { } + /// Gained buff Info. + public virtual void OnGainBuffEffect(uint sourceId, Status Status) { } /// /// Will be called when a buff is removed from a game object. This method will only be called if a script is enabled. /// /// Source object ID of buff removal. - /// Array of removed buff Infos. - public virtual void OnRemoveBuffEffect(uint sourceId, IReadOnlyList removeStatusInfos) { } + /// Removed buff Infos. + public virtual void OnRemoveBuffEffect(uint sourceId, Status Status) { } + + /// + /// Will be called when a buff is updated on a game object. This method will only be called if a script is enabled. + /// + /// Source object ID of buff update. + /// Updated status. + public virtual void OnUpdateBuffEffect(uint sourceId, Status status) { } /// /// Returns appropriate string depending on current game language. If not defined for current language, will return first defined string. diff --git a/Splatoon/Structures/RecordedStatus.cs b/Splatoon/Structures/RecordedStatus.cs index 413fb993..f32c1b0f 100644 --- a/Splatoon/Structures/RecordedStatus.cs +++ b/Splatoon/Structures/RecordedStatus.cs @@ -6,7 +6,7 @@ public readonly record struct RecordedStatus public readonly uint StatusId; public readonly ushort Param; - public RecordedStatus(string statusName, uint statusId, ushort param) + public RecordedStatus(string statusName, uint statusId, byte stackCount, ushort param) { StatusName = statusName; StatusId = statusId; diff --git a/SplatoonScripts/Tests/RedmoonTest1.cs b/SplatoonScripts/Tests/RedmoonTest1.cs new file mode 100644 index 00000000..063659cb --- /dev/null +++ b/SplatoonScripts/Tests/RedmoonTest1.cs @@ -0,0 +1,85 @@ +using ECommons.DalamudServices; +using ECommons.ImGuiMethods; +using FFXIVClientStructs.FFXIV.Client.Game; +using FFXIVClientStructs.FFXIV.Client.Game.Event; +using FFXIVClientStructs.FFXIV.Client.Game.Object; +using ImGuiNET; +using Microsoft.VisualBasic; +using Splatoon.SplatoonScripting; +using System.Collections.Generic; + + +namespace SplatoonScriptsOfficial.Tests; +internal unsafe class RedmoonTest1 :SplatoonScript +{ + public override HashSet? ValidTerritories => null; + + public override void OnSettingsDraw() + { + var gom = GameObjectManager.Instance(); + EventFramework* eventFrameworkPtr = EventFramework.Instance(); + + if(eventFrameworkPtr == null) + { + ImGui.Text("EventFramework is null"); + return; + } + if(gom == null || gom->Objects.IndexSorted.Length == 0) + { + ImGui.Text("GameObjectManager is null or has no objects"); + return; + } + + if(ImGuiEx.CollapsingHeader("Sorted list")) + { + for(int objectIndex = 0; objectIndex < gom->Objects.IndexSorted.Length; ++objectIndex) + { + var obj = gom->Objects.IndexSorted[objectIndex].Value; + if(obj == null) + { + ImGuiNET.ImGui.Text($"Object {objectIndex}: null"); + continue; + } + ImGuiNET.ImGui.Text($"Object {objectIndex}: 0x{Conversion.Hex(obj->EntityId)} {obj->NameString.ToString()}"); + } + } + if(ImGuiEx.CollapsingHeader("SortEntityId list")) + { + for(int objectIndex = 0; objectIndex < gom->Objects.EntityIdSorted.Length; ++objectIndex) + { + var obj = gom->Objects.EntityIdSorted[objectIndex].Value; + if(obj == null) + { + ImGuiNET.ImGui.Text($"Object {objectIndex}: null"); + continue; + } + ImGuiNET.ImGui.Text($"Object {objectIndex}: 0x{Conversion.Hex(obj->EntityId)} {obj->NameString.ToString()}"); + } + } + if(ImGuiEx.CollapsingHeader("MyStatusList")) + { + StatusManager* sm = (StatusManager*)Svc.ClientState.LocalPlayer.StatusList.Address; + Status* statusArray = (Status*)((byte*)sm + 0x08); + if(sm == null) + { + ImGui.Text("StatusManager is null"); + return; + } + if(statusArray == null) + { + ImGui.Text("StatusArray is null"); + return; + } + for(int statusIndex = 0; statusIndex < sm->NumValidStatuses; ++statusIndex) + { + ImGui.Text($"Status {statusIndex}: 0x{Conversion.Hex((&statusArray[statusIndex])->StatusId)}"); + } + } + if(ImGuiEx.CollapsingHeader("EventFramework")) + { + ImGui.Text(eventFrameworkPtr->GetContentDirector() == null ? "CurrentDirector is null" : "CurrentDirector is not null"); + ImGui.Text(eventFrameworkPtr->GetInstanceContentDirector() == null ? "InstanceContentDirector is null" : "InstanceContentDirector is not null"); + ImGui.Text(eventFrameworkPtr->GetPublicContentDirector() == null ? "PublicContentDirector is null" : "PublicContentDirector is not null"); + } + } +}