From d13d8469f3cceefba131650e4607eacf6d7f0331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <1123993881@qq.com> Date: Wed, 28 Feb 2024 11:13:52 +0800 Subject: [PATCH] fix: add more thing to sync the raid time. --- .../Actions/ActionCooldownInfo.cs | 13 +++- RotationSolver.Basic/Data/TimelineItem.cs | 75 +++++++++++++++++-- RotationSolver.Basic/DataCenter.cs | 18 ++++- RotationSolver.Basic/Helpers/ObjectHelper.cs | 40 +++++++--- .../StaticCodeGenerator.cs | 42 +++++------ RotationSolver/Commands/RSCommands_Actions.cs | 6 +- RotationSolver/Helpers/DownloadHelper.cs | 3 +- RotationSolver/UI/RotationConfigWindow.cs | 6 +- .../UI/RotationConfigWindow_Config.cs | 8 +- RotationSolver/Updaters/ActionUpdater.cs | 13 +++- RotationSolver/Updaters/RaidTimeUpdater.cs | 54 ++++++++----- RotationSolver/Updaters/RotationUpdater.cs | 9 +-- RotationSolver/Updaters/SocialUpdater.cs | 29 +------ RotationSolver/Updaters/StateUpdater.cs | 2 +- RotationSolver/Updaters/TargetUpdater.cs | 38 ++++++++-- RotationSolver/Watcher.cs | 63 +++++++++++++--- 16 files changed, 304 insertions(+), 115 deletions(-) diff --git a/RotationSolver.Basic/Actions/ActionCooldownInfo.cs b/RotationSolver.Basic/Actions/ActionCooldownInfo.cs index ddcdb4a0a..807750ad8 100644 --- a/RotationSolver.Basic/Actions/ActionCooldownInfo.cs +++ b/RotationSolver.Basic/Actions/ActionCooldownInfo.cs @@ -58,7 +58,7 @@ namespace RotationSolver.Basic.Actions; /// /// /// - public float RecastTimeRemainOneCharge => RecastTimeRemainOneChargeRaw + DataCenter.WeaponRemain; + public float RecastTimeRemainOneCharge => RecastTimeRemainOneChargeRaw - DataCenter.WeaponRemain; float RecastTimeRemainOneChargeRaw => RecastTimeRemain % RecastTimeOneChargeRaw; @@ -130,6 +130,17 @@ public bool WillHaveOneChargeGCD(uint gcdCount = 0, float offset = 0) public bool WillHaveOneCharge(float remain) => HasOneCharge || RecastTimeRemainOneCharge <= remain; + /// + /// Is this action used after several time. + /// + /// + /// + public bool JustUsedAfter(float time) + { + var elapsed = RecastTimeElapsedRaw % RecastTimeOneChargeRaw; + return elapsed + DataCenter.WeaponRemain < time; + } + internal bool CooldownCheck(bool isEmpty, bool onLastAbility, bool ignoreClippingCheck, byte gcdCountForAbility) { if (!_action.Info.IsGeneralGCD) diff --git a/RotationSolver.Basic/Data/TimelineItem.cs b/RotationSolver.Basic/Data/TimelineItem.cs index 8b1594e8f..0de27c62e 100644 --- a/RotationSolver.Basic/Data/TimelineItem.cs +++ b/RotationSolver.Basic/Data/TimelineItem.cs @@ -1,11 +1,75 @@ -using System.Text.RegularExpressions; +using ECommons.DalamudServices; +using Newtonsoft.Json.Linq; +using System.Text.RegularExpressions; namespace RotationSolver.Basic.Data; -internal readonly struct TimelineItem(float time, string name, params string[] ids) +internal enum TimelineType : byte { + Unknown, + InCombat, + GameLog, + Ability, + StartsUsing, + SystemLogMessage, + ActorControl, +} + +internal readonly struct TimelineItem(float time, string name, TimelineType type, string[] ids, JObject? obj) +{ + public TimelineType Type => type; + public float Time => time; - public bool IsShown => !name.StartsWith("--") && !name.EndsWith("--"); + public JObject? Object => obj; + + public string Name => name; + + public bool IsShown => name is not "--Reset--" and not "--sync--"; + + public string this[string propertyName] => Object?[propertyName]?.ToString() ?? string.Empty; + + public TimelineItem(float time, string name, string type, string[] ids, JObject? obj) + : this(time, name, GetTypeFromName(type), ids, obj) + { + + } + + private static TimelineType GetTypeFromName(string type) + { + switch(type) + { + case "": + return TimelineType.Unknown; + + case "ActorControl": + return TimelineType.ActorControl; //TODO: how to check that command. + + case "InCombat": + return TimelineType.InCombat; + + case "GameLog": + return TimelineType.GameLog; + + case "Ability": + return TimelineType.Ability; + + case "StartsUsing": + return TimelineType.StartsUsing; + + case "SystemLogMessage": + return TimelineType.SystemLogMessage; //TODO: a6s for testing + + default: + Svc.Log.Warning($"New timelinetype: {type}"); + return TimelineType.Unknown; + } + } + + public void UpdateRaidTimeOffset() + { + DataCenter.RaidTimeRaw = Time; + Svc.Log.Debug($"Reset the {nameof(DataCenter.RaidTimeRaw)} to {DataCenter.RaidTimeRaw}."); + } public bool IsIdMatched(uint id) { @@ -16,8 +80,9 @@ public override string ToString() { return $""" IsShown: {IsShown}, - Time: {time}, - Name: {name}, + Time: {Time}, + Name: {Name}, + Type: {Type}, Ids: {string.Join(", ", ids)} """; } diff --git a/RotationSolver.Basic/DataCenter.cs b/RotationSolver.Basic/DataCenter.cs index 983b300f7..2ff63fd95 100644 --- a/RotationSolver.Basic/DataCenter.cs +++ b/RotationSolver.Basic/DataCenter.cs @@ -263,8 +263,19 @@ internal static SpecialCommandType SpecialType public static bool NotInCombatDelay => _notInCombatDelay.Delay(!InCombat); internal static float CombatTimeRaw { get; set; } - internal static float RaidTimeOffset { get; set; } = 0; - internal static float RaidTimeRaw => CombatTimeRaw + RaidTimeOffset; + private static DateTime _startRaidTime = DateTime.MinValue; + internal static float RaidTimeRaw + { + get + { + if (_startRaidTime == DateTime.MinValue) return 0; + return (float)(DateTime.Now - _startRaidTime).TotalSeconds; + } + set + { + _startRaidTime = DateTime.Now - TimeSpan.FromSeconds(value); + } + } internal static TimelineItem[] TimelineItems { get; set; } = []; @@ -423,4 +434,7 @@ internal static void AddDamageRec(float damageRatio) internal static DateTime KnockbackFinished { get; set; } = DateTime.MinValue; internal static DateTime KnockbackStart { get; set; } = DateTime.MinValue; #endregion + + internal static SortedList AuthorHashes { get; set; } = []; + internal static string[] ContributorsHash { get; set; } = []; } diff --git a/RotationSolver.Basic/Helpers/ObjectHelper.cs b/RotationSolver.Basic/Helpers/ObjectHelper.cs index 0e121e32f..b30468bd6 100644 --- a/RotationSolver.Basic/Helpers/ObjectHelper.cs +++ b/RotationSolver.Basic/Helpers/ObjectHelper.cs @@ -9,6 +9,8 @@ using FFXIVClientStructs.FFXIV.Common.Component.BGCollision; using Lumina.Excel.GeneratedSheets; using RotationSolver.Basic.Configuration; +using System.Security.Cryptography; +using System.Text; using System.Text.RegularExpressions; namespace RotationSolver.Basic.Helpers; @@ -87,17 +89,16 @@ internal static bool IsAttackable(this BattleChara battleChara) if (names.Any(n => !string.IsNullOrEmpty(n) && new Regex(n).Match(battleChara.Name.TextValue).Success)) return false; - //if (gameObject is PlayerCharacter p) - //{ - // var hash = SocialUpdater.EncryptString(p); - - // //Don't attack authors!! - // if (RotationUpdater.AuthorHashes.ContainsKey(hash)) return false; + if (battleChara is PlayerCharacter p) + { + var hash = EncryptString(p); - // //Don't attack contributors!! - // if (DownloadHelper.ContributorsHash.Contains(hash)) return false; - //} + //Don't attack authors!! + if (DataCenter.AuthorHashes.ContainsKey(hash)) return false; + //Don't attack contributors!! + if (DataCenter.ContributorsHash.Contains(hash)) return false; + } //Fate if (DataCenter.TerritoryContentType != TerritoryContentType.Eureka) @@ -132,6 +133,27 @@ internal static bool IsAttackable(this BattleChara battleChara) }; } + + internal static string EncryptString(this PlayerCharacter player) + { + if (player == null) return string.Empty; + + try + { + byte[] inputByteArray = Encoding.UTF8.GetBytes(player.HomeWorld.GameData!.InternalName.ToString() + + " - " + player.Name.ToString() + "U6Wy.zCG"); + + var tmpHash = MD5.HashData(inputByteArray); + var retB = Convert.ToBase64String(tmpHash); + return retB; + } + catch (Exception ex) + { + Svc.Log.Warning(ex, "Failed to read the player's name and world."); + return string.Empty; + } + } + internal static unsafe bool IsInEnemiesList(this BattleChara battleChara) { var addons = Service.GetAddons(); diff --git a/RotationSolver.SourceGenerators/StaticCodeGenerator.cs b/RotationSolver.SourceGenerators/StaticCodeGenerator.cs index f1a261659..a95a604b9 100644 --- a/RotationSolver.SourceGenerators/StaticCodeGenerator.cs +++ b/RotationSolver.SourceGenerators/StaticCodeGenerator.cs @@ -25,29 +25,29 @@ private static void Execute(SourceProductionContext context) GenerateActionCate(context); GenerateBaseRotation(context); GenerateRotations(context); - //GenerateOpCode(context); + GenerateOpCode(context); } - //private static void GenerateOpCode(SourceProductionContext context) - //{ - // var code = $$""" - // namespace RotationSolver.Basic.Data; - - // /// - // /// The opcode - // /// - // public enum OpCode : ushort - // { - // /// - // /// - // /// - // None = 0, - // {{Properties.Resources.OpCode.Table()}} - // } - // """; - - // context.AddSource("OpCode.g.cs", code); - //} + private static void GenerateOpCode(SourceProductionContext context) + { + var code = $$""" + namespace RotationSolver.Basic.Data; + + /// + /// The opcode + /// + public enum OpCode : ushort + { + /// + /// + /// + None = 0, + {{Properties.Resources.OpCode.Table()}} + } + """; + + context.AddSource("OpCode.g.cs", code); + } private static void GenerateStatus(SourceProductionContext context) { diff --git a/RotationSolver/Commands/RSCommands_Actions.cs b/RotationSolver/Commands/RSCommands_Actions.cs index e901d3b01..f99ed22ec 100644 --- a/RotationSolver/Commands/RSCommands_Actions.cs +++ b/RotationSolver/Commands/RSCommands_Actions.cs @@ -55,11 +55,11 @@ public static void DoAction() && act1.TargetInfo.IsSingleTarget && act1.Target?.Target is PlayerCharacter p && p != Player.Object) { - var hash = SocialUpdater.EncryptString(p); + var hash = p.EncryptString(); //Don't attack authors and contributors!! - if ((RotationUpdater.AuthorHashes.ContainsKey(hash) - || DownloadHelper.ContributorsHash.Contains(hash))) + if ((DataCenter.AuthorHashes.ContainsKey(hash) + || DataCenter.ContributorsHash.Contains(hash))) { Svc.Chat.PrintError($"Please don't attack RS developers with RS by {act1}!"); return; diff --git a/RotationSolver/Helpers/DownloadHelper.cs b/RotationSolver/Helpers/DownloadHelper.cs index 87f2b5c31..6a7dd1d63 100644 --- a/RotationSolver/Helpers/DownloadHelper.cs +++ b/RotationSolver/Helpers/DownloadHelper.cs @@ -10,7 +10,6 @@ public static class DownloadHelper /// Maximum HP and MP are increased. /// public static string[] LinkLibraries { get; private set; } = []; - public static string[] ContributorsHash { get; private set; } = []; public static string[] UsersHash { get; private set; } = []; public static string[] Supporters { get; private set; } = []; public static IncompatiblePlugin[] IncompatiblePlugins { get; private set; } = []; @@ -20,7 +19,7 @@ public static async Task DownloadAsync() LinkLibraries = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/downloadList.json") ?? []; IncompatiblePlugins = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/IncompatiblePlugins.json") ?? []; - ContributorsHash = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/ContributorsHash.json") ?? []; + DataCenter.ContributorsHash = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/ContributorsHash.json") ?? []; UsersHash = await DownloadOneAsync($"https://raw.githubusercontent.com/{Service.USERNAME}/{Service.REPO}/main/Resources/UsersHash.json") ?? []; diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs index ab3a1bd10..ff5d634eb 100644 --- a/RotationSolver/UI/RotationConfigWindow.cs +++ b/RotationSolver/UI/RotationConfigWindow.cs @@ -2432,8 +2432,8 @@ private static unsafe void DrawStatus() } private static unsafe void DrawParty() { - ImGui.Text("Party: " + DataCenter.PartyMembers.Count().ToString()); - ImGui.Text("Alliance: " + DataCenter.AllianceMembers.Count().ToString()); + ImGui.Text("Party: " + DataCenter.PartyMembers.Length.ToString()); + ImGui.Text("Alliance: " + DataCenter.AllianceMembers.Length.ToString()); ImGui.Text("PartyMembersAverHP: " + DataCenter.PartyMembersAverHP.ToString()); @@ -2495,7 +2495,7 @@ private static unsafe void DrawTargetData() } ImGui.Text("All: " + DataCenter.AllTargets.Count().ToString()); - ImGui.Text("Hostile: " + DataCenter.AllHostileTargets.Count().ToString()); + ImGui.Text("Hostile: " + DataCenter.AllHostileTargets.Length.ToString()); foreach (var item in DataCenter.AllHostileTargets) { ImGui.Text(item.Name.ToString()); diff --git a/RotationSolver/UI/RotationConfigWindow_Config.cs b/RotationSolver/UI/RotationConfigWindow_Config.cs index 8d3a4bbc4..109a6673b 100644 --- a/RotationSolver/UI/RotationConfigWindow_Config.cs +++ b/RotationSolver/UI/RotationConfigWindow_Config.cs @@ -220,7 +220,7 @@ private static void DrawBasicNamedConditions() { if (!DataCenter.RightSet.NamedConditions.Any(c => string.IsNullOrEmpty(c.Name))) { - DataCenter.RightSet.NamedConditions = DataCenter.RightSet.NamedConditions.Append((string.Empty, new ConditionSet())).ToArray(); + DataCenter.RightSet.NamedConditions = [.. DataCenter.RightSet.NamedConditions, (string.Empty, new ConditionSet())]; } ImGui.Spacing(); @@ -271,13 +271,13 @@ private static void DrawBasicOthers() if (Service.Config.SayHelloToAll) { - var str = SocialUpdater.EncryptString(Player.Object); + var str = Player.Object.EncryptString(); ImGui.SetNextItemWidth(ImGui.CalcTextSize(str).X + 10); ImGui.InputText("That is your HASH:", ref str, 100); - if (!DownloadHelper.ContributorsHash.Contains(str) + if (!DataCenter.ContributorsHash.Contains(str) && !DownloadHelper.UsersHash.Contains(str) - && !RotationUpdater.AuthorHashes.ContainsKey(str)) + && !DataCenter.AuthorHashes.ContainsKey(str)) { if (ImGui.Button("DM your Hash to ArchiTed for being greeted.")) { diff --git a/RotationSolver/Updaters/ActionUpdater.cs b/RotationSolver/Updaters/ActionUpdater.cs index d1cf5cc6f..a32e4f8e2 100644 --- a/RotationSolver/Updaters/ActionUpdater.cs +++ b/RotationSolver/Updaters/ActionUpdater.cs @@ -5,6 +5,8 @@ using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.UI.Agent; using RotationSolver.Commands; +using System.Text.RegularExpressions; +using System.Windows.Forms; namespace RotationSolver.Updaters; @@ -143,10 +145,19 @@ private static void UpdateCombatTime() if (_startCombatTime == DateTime.MinValue) { DataCenter.CombatTimeRaw = 0; - DataCenter.RaidTimeOffset = 0; } else { + if (DataCenter.CombatTimeRaw == 0) + { + foreach (var item in DataCenter.TimelineItems) + { + if (item.Type is not TimelineType.InCombat) continue; + + item.UpdateRaidTimeOffset(); + break; + } + } DataCenter.CombatTimeRaw = (float)(DateTime.Now - _startCombatTime).TotalSeconds; } } diff --git a/RotationSolver/Updaters/RaidTimeUpdater.cs b/RotationSolver/Updaters/RaidTimeUpdater.cs index d698c77d7..bc5b18388 100644 --- a/RotationSolver/Updaters/RaidTimeUpdater.cs +++ b/RotationSolver/Updaters/RaidTimeUpdater.cs @@ -16,26 +16,25 @@ internal static void Disable() Svc.ClientState.TerritoryChanged -= ClientState_TerritoryChanged; } - private static void ClientState_TerritoryChanged(ushort obj) + private static async void ClientState_TerritoryChanged(ushort obj) { try { - UpdateRaidTime("06-ew/raid/p9s.txt"); + DataCenter.TimelineItems = await UpdateRaidTime("06-ew/raid/p9s.txt"); } catch (Exception e) { Svc.Log.Warning(e, "Failed to update the raid timeline!"); } } - static async void UpdateRaidTime(string path) + static async Task UpdateRaidTime(string path) { using var client = new HttpClient(); var message = await client.GetAsync("https://raw.githubusercontent.com/OverlayPlugin/cactbot/main/ui/raidboss/data/" + path); if (!message.IsSuccessStatusCode) { - DataCenter.TimelineItems = []; - return; + return []; } var str = await message.Content.ReadAsStringAsync(); @@ -45,32 +44,51 @@ static async void UpdateRaidTime(string path) { try { - var header = TimeHeader().Match(timelineItem.Value); - var action = JObject.Parse(ActionGetter().Match(timelineItem.Value).Value)["id"]; - var time = float.Parse(Time().Match(header.Value).Value); - var name = Name().Match(header.Value).Value[1..^1]; + var timeline = timelineItem.Value; + var header = TimeHeader().Match(timeline).Value; + var time = float.Parse(Time().Match(header).Value); + var name = Name().Match(header).Value[1..^1]; + + var item = JObject.Parse(ActionGetter().Match(timeline).Value); string[] ids = []; - if (action is JArray array) - { - ids = [..array.Select(i => i.ToString())]; - } - else + if (item != null) { - ids = [action?.ToString() ?? string.Empty]; + var id = item["id"]; + if (id is JArray array) + { + ids = [.. array.Select(i => i.ToString())]; + } + else + { + ids = [id?.ToString() ?? string.Empty]; + } } - result.Add(new (time, name, ids)); + var rest = timeline[header.Length..]; + var type = Type().Match(rest)?.Value[1..^1] ?? string.Empty; + + result.Add(new (time, name, type, ids, item)); } catch (Exception ex) { Svc.Log.Warning(ex, "sth wrong with matching!"); } } - DataCenter.TimelineItems = [..result]; + +#if DEBUG + foreach (var item in result) + { + Svc.Log.Debug(item.ToString()); + } +#endif + return [..result]; } - [GeneratedRegex("\\d+\\.\\d.*Ability.*")] + [GeneratedRegex(" .*? ")] + private static partial Regex Type(); + + [GeneratedRegex("\\d+\\.\\d.*")] private static partial Regex TimeLineItem(); [GeneratedRegex("^\\d+\\.\\d")] diff --git a/RotationSolver/Updaters/RotationUpdater.cs b/RotationSolver/Updaters/RotationUpdater.cs index 5b04457e8..60a10856f 100644 --- a/RotationSolver/Updaters/RotationUpdater.cs +++ b/RotationSolver/Updaters/RotationUpdater.cs @@ -14,7 +14,6 @@ internal static class RotationUpdater internal record CustomRotationGroup(Job JobId, Job[] ClassJobIds, Type[] Rotations); internal static SortedList CustomRotationsDict { get; private set; } = []; - internal static SortedList AuthorHashes { get; private set; } = []; internal static CustomRotationGroup[] CustomRotations { get; set; } = []; internal static SortedList DutyRotations { get; set; } = []; @@ -99,7 +98,7 @@ private static void LoadRotationsFromLocal(string relayFolder) } } - AuthorHashes = []; + DataCenter.AuthorHashes = []; foreach (var assembly in assemblies) { try @@ -112,13 +111,13 @@ private static void LoadRotationsFromLocal(string relayFolder) var value = $"{assembly.GetInfo().Author} - {assembly.GetInfo().Name}"; - if (AuthorHashes.ContainsKey(key)) + if (DataCenter.AuthorHashes.ContainsKey(key)) { - AuthorHashes[key] += $", {value}"; + DataCenter.AuthorHashes[key] += $", {value}"; } else { - AuthorHashes.Add(key, value); + DataCenter.AuthorHashes.Add(key, value); } } } diff --git a/RotationSolver/Updaters/SocialUpdater.cs b/RotationSolver/Updaters/SocialUpdater.cs index a2a112c89..e724f9706 100644 --- a/RotationSolver/Updaters/SocialUpdater.cs +++ b/RotationSolver/Updaters/SocialUpdater.cs @@ -13,8 +13,6 @@ using RotationSolver.Helpers; using RotationSolver.Localization; using System.Diagnostics.CodeAnalysis; -using System.Security.Cryptography; -using System.Text; namespace RotationSolver.Updaters; @@ -168,21 +166,21 @@ private static async void SayHelloToUsers() #else .Where(c => c.ObjectId != Player.Object.ObjectId) #endif - .Select(player => (player, EncryptString(player))) + .Select(player => (player, player.EncryptString())) .Where(pair => !saidAuthors.Contains(pair.Item2) && !OtherConfiguration.RotationSolverRecord.SaidUsers.Contains(pair.Item2)); IEnumerable entities = players .Select(c => { - if (!RotationUpdater.AuthorHashes.TryGetValue(c.Item2, out var nameDesc)) nameDesc = string.Empty; + if (!DataCenter.AuthorHashes.TryGetValue(c.Item2, out var nameDesc)) nameDesc = string.Empty; return (c.player, nameDesc); }) .Where(p => !string.IsNullOrEmpty(p.nameDesc)) .Select(p => new RotationAuthorChatEntity(p.player, p.nameDesc)); entities = entities.Union(players - .Where(p => DownloadHelper.ContributorsHash.Contains(p.Item2)) + .Where(p => DataCenter.ContributorsHash.Contains(p.Item2)) .Select(p => new ContributorChatEntity(p.player)), _comparer); if (Service.Config.SayHelloToUsers) @@ -218,25 +216,6 @@ private static async void SayHelloToUsers() } } - internal static string EncryptString(PlayerCharacter player) - { - if (player == null) return string.Empty; - - try - { - byte[] inputByteArray = Encoding.UTF8.GetBytes(player.HomeWorld.GameData!.InternalName.ToString() - + " - " + player.Name.ToString() + "U6Wy.zCG"); - - var tmpHash = MD5.HashData(inputByteArray); - var retB = Convert.ToBase64String(tmpHash); - return retB; - } - catch (Exception ex) - { - Svc.Log.Warning(ex, "Failed to read the player's name and world."); - return string.Empty; - } - } internal abstract class ChatEntity(PlayerCharacter character) : IDisposable { @@ -276,7 +255,7 @@ public bool CanTarget public void Dispose() { OtherConfiguration.RotationSolverRecord.SayingHelloCount++; - var hash = EncryptString(player); + var hash = player.EncryptString(); saidAuthors.Add(hash); if (Service.Config.JustSayHelloOnce) { diff --git a/RotationSolver/Updaters/StateUpdater.cs b/RotationSolver/Updaters/StateUpdater.cs index f3eea3fa0..81c6cb9d6 100644 --- a/RotationSolver/Updaters/StateUpdater.cs +++ b/RotationSolver/Updaters/StateUpdater.cs @@ -69,7 +69,7 @@ private static AutoStatus StatusFromAutomatic() var canHealAreaAbility = singleAbility > 2; var canHealAreaSpell = singleSpell > 2; - if (DataCenter.PartyMembers.Count() > 2) + if (DataCenter.PartyMembers.Length > 2) { //TODO: Beneficial area status. var ratio = GetHealingOfTimeRatio(Player.Object, StatusHelper.AreaHots); diff --git a/RotationSolver/Updaters/TargetUpdater.cs b/RotationSolver/Updaters/TargetUpdater.cs index 873cbf036..314d90d4b 100644 --- a/RotationSolver/Updaters/TargetUpdater.cs +++ b/RotationSolver/Updaters/TargetUpdater.cs @@ -16,13 +16,41 @@ static readonly ObjectListDelay _raiseAllTargets = new(() => Service.Config.RaiseDelay); internal unsafe static void UpdateTarget() { - DataCenter.AllTargets.Delay(Svc.Objects.GetObjectInRadius(30).OfType()); + var battles = Svc.Objects.OfType(); + + DataCenter.AllTargets.Delay(battles.GetObjectInRadius(30)); UpdateHostileTargets(DataCenter.AllTargets); UpdateFriends(DataCenter.AllTargets .Where(b => b.Character()->CharacterData.OnlineStatus != 15 //Removed the one watching cutscene. && b.IsTargetable //Removed the one can't target. )); - UpdateNamePlate(Svc.Objects.OfType()); + UpdateNamePlate(battles); + } + + static readonly Dictionary _castingTargets = []; + private static void UpdateCastingRefine(IEnumerable allTargets) + { + foreach (BattleChara b in allTargets) + { + if (!_castingTargets.TryGetValue(b.ObjectId, out var isLastCasting)) continue; + if (isLastCasting) continue; + + if (!b.IsCasting) continue; + + foreach (var item in DataCenter.TimelineItems) + { + if (item.Type is not TimelineType.StartsUsing) continue; + if (!item.IsIdMatched(b.CastActionId)) continue; + + item.UpdateRaidTimeOffset(); + break; + } + } + + foreach (BattleChara b in allTargets) + { + _castingTargets[b.ObjectId] = b.IsCasting; + } } private static DateTime _lastUpdateTimeToKill = DateTime.MinValue; @@ -96,13 +124,13 @@ private unsafe static void UpdateHostileTargets(IEnumerable allTarg if (b is PlayerCharacter p) { - var hash = SocialUpdater.EncryptString(p); + var hash = p.EncryptString(); //Don't attack authors!! - if (RotationUpdater.AuthorHashes.ContainsKey(hash)) return false; + if (DataCenter.AuthorHashes.ContainsKey(hash)) return false; //Don't attack contributors!! - if (DownloadHelper.ContributorsHash.Contains(hash)) return false; + if (DataCenter.ContributorsHash.Contains(hash)) return false; } return true; }).ToArray(); diff --git a/RotationSolver/Watcher.cs b/RotationSolver/Watcher.cs index 2d1359adb..4ec1086d1 100644 --- a/RotationSolver/Watcher.cs +++ b/RotationSolver/Watcher.cs @@ -8,6 +8,7 @@ using FFXIVClientStructs.FFXIV.Client.Game; using Lumina.Excel.GeneratedSheets; using RotationSolver.Basic.Configuration; +using System.Runtime.InteropServices; using System.Text.RegularExpressions; using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; @@ -63,20 +64,61 @@ public static void Enable() #endif }); - //Svc.GameNetwork.NetworkMessage += GameNetwork_NetworkMessage; + Svc.GameNetwork.NetworkMessage += GameNetwork_NetworkMessage; + Svc.Chat.ChatMessage += Chat_ChatMessage; } -// private static void GameNetwork_NetworkMessage(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, Dalamud.Game.Network.NetworkMessageDirection direction) -// { -// if (direction != Dalamud.Game.Network.NetworkMessageDirection.ZoneDown) return; -// OpCode op = (OpCode)opCode; + private static void Chat_ChatMessage(Dalamud.Game.Text.XivChatType type, uint senderId, ref Dalamud.Game.Text.SeStringHandling.SeString sender, ref Dalamud.Game.Text.SeStringHandling.SeString message, ref bool isHandled) + { + foreach (var item in DataCenter.TimelineItems) + { + if (item.Type is not TimelineType.GameLog) continue; + + var typeString = ((uint)type).ToString("X4"); + if (!new Regex(item["code"]).IsMatch(typeString)) continue; + + //TODO: multi language. + if (!new Regex(item["line"]).IsMatch(message.TextValue)) continue; + item.UpdateRaidTimeOffset(); + break; + } + } + + private static void GameNetwork_NetworkMessage(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, Dalamud.Game.Network.NetworkMessageDirection direction) + { + if (direction != Dalamud.Game.Network.NetworkMessageDirection.ZoneDown) return; + OpCode op = (OpCode)opCode; + + switch (op) + { + case OpCode.SystemLogMessage: + OnSystemLogMessage(dataPtr); + break; + } //#if DEBUG // var source = Svc.Objects.SearchById(sourceActorId)?.Name.TextValue; // var target = Svc.Objects.SearchById(targetActorId)?.Name.TextValue; // Svc.Log.Debug($"From {source} to {target} by {op}."); //#endif -// } + } + + private static void OnSystemLogMessage(IntPtr dataPtr) + { + var bytes = new byte[32]; + Marshal.Copy(dataPtr, bytes, 0, 32); + Svc.Log.Debug("SystemLogMessage: " + Convert.ToHexString(bytes)); + + //TODO: the refine to the raidtime. + + //if (logId == DataIds.SystemLogPomanderUsed) + // OnPomanderUsed((Pomander)Marshal.ReadByte(dataPtr, 16)); + //else if (logId == DataIds.SystemLogDutyEnded) + // ExitDeepDungeon(); + //else if (logId == DataIds.SystemLogTransferenceInitiated) + // nextFloorTransfer = true; + + } public static void Disable() { @@ -88,7 +130,8 @@ public static void Disable() MapEffect.Dispose(); ActionEffect.ActionEffectEvent -= ActionFromEnemy; ActionEffect.ActionEffectEvent -= ActionFromSelf; - //Svc.GameNetwork.NetworkMessage -= GameNetwork_NetworkMessage; + Svc.GameNetwork.NetworkMessage -= GameNetwork_NetworkMessage; + Svc.Chat.ChatMessage -= Chat_ChatMessage; } private static IntPtr ActorVfxNewHandler(string path, IntPtr a2, IntPtr a3, float a4, char a5, ushort a6, char a7) @@ -170,10 +213,10 @@ private static void ActionFromEnemy(ActionEffectSet set) { foreach (var item in DataCenter.TimelineItems) { + if (item.Type is not TimelineType.Ability) continue; if (!item.IsIdMatched(set.Action?.RowId ?? 0)) continue; - DataCenter.RaidTimeOffset = item.Time - DataCenter.CombatTimeRaw; - Svc.Log.Debug($"Reset the {nameof(DataCenter.RaidTimeOffset)} to {DataCenter.RaidTimeOffset} by the action {set.Action}"); + item.UpdateRaidTimeOffset(); break; } @@ -213,7 +256,7 @@ private static void ActionFromEnemy(ActionEffectSet set) } } - if (set.Header.ActionType == ActionType.Action && DataCenter.PartyMembers.Length >= 4 && set.Action.Cast100ms > 0) + if (set.Header.ActionType == ActionType.Action && DataCenter.PartyMembers.Length >= 4 && set.Action?.Cast100ms > 0) { var type = set.Action.GetActionCate();