Check for unexpected party members when entering instanced duties
authorLiza Carvelli <liza@carvel.li>
Sat, 22 Feb 2025 22:08:58 +0000 (23:08 +0100)
committerLiza Carvelli <liza@carvel.li>
Sat, 22 Feb 2025 22:08:58 +0000 (23:08 +0100)
Questionable/Controller/Utils/PartyWatchDog.cs [new file with mode: 0644]
Questionable/DalamudInitializer.cs
Questionable/QuestionablePlugin.cs

diff --git a/Questionable/Controller/Utils/PartyWatchDog.cs b/Questionable/Controller/Utils/PartyWatchDog.cs
new file mode 100644 (file)
index 0000000..9e7a0cc
--- /dev/null
@@ -0,0 +1,167 @@
+using System;
+using Dalamud.Plugin.Services;
+using FFXIVClientStructs.FFXIV.Client.Game;
+using FFXIVClientStructs.FFXIV.Client.Game.Group;
+using JetBrains.Annotations;
+using Microsoft.Extensions.Logging;
+
+namespace Questionable.Controller.Utils;
+
+internal sealed class PartyWatchDog : IDisposable
+{
+    private readonly QuestController _questController;
+    private readonly IClientState _clientState;
+    private readonly IChatGui _chatGui;
+    private readonly ILogger<PartyWatchDog> _logger;
+
+    private ushort? _uncheckedTeritoryId;
+
+    public PartyWatchDog(QuestController questController, IClientState clientState, IChatGui chatGui,
+        ILogger<PartyWatchDog> logger)
+    {
+        _questController = questController;
+        _clientState = clientState;
+        _chatGui = chatGui;
+        _logger = logger;
+
+        _clientState.TerritoryChanged += TerritoryChanged;
+    }
+
+    private unsafe void TerritoryChanged(ushort newTerritoryId)
+    {
+        var intendedUse = (ETerritoryIntendedUseEnum)GameMain.Instance()->CurrentTerritoryIntendedUseId;
+        switch (intendedUse)
+        {
+            case ETerritoryIntendedUseEnum.Gaol:
+            case ETerritoryIntendedUseEnum.Frontline:
+            case ETerritoryIntendedUseEnum.LordOfVerminion:
+            case ETerritoryIntendedUseEnum.Diadem:
+            case ETerritoryIntendedUseEnum.CrystallineConflict:
+            case ETerritoryIntendedUseEnum.Battlehall:
+            case ETerritoryIntendedUseEnum.CrystallineConflict2:
+            case ETerritoryIntendedUseEnum.DeepDungeon:
+            case ETerritoryIntendedUseEnum.TreasureMapDuty:
+            case ETerritoryIntendedUseEnum.Diadem2:
+            case ETerritoryIntendedUseEnum.RivalWings:
+            case ETerritoryIntendedUseEnum.Eureka:
+            case ETerritoryIntendedUseEnum.LeapOfFaith:
+            case ETerritoryIntendedUseEnum.OceanFishing:
+            case ETerritoryIntendedUseEnum.Diadem3:
+            case ETerritoryIntendedUseEnum.Bozja:
+            case ETerritoryIntendedUseEnum.Battlehall2:
+            case ETerritoryIntendedUseEnum.Battlehall3:
+            case ETerritoryIntendedUseEnum.LargeScaleRaid:
+            case ETerritoryIntendedUseEnum.LargeScaleSavageRaid:
+            case ETerritoryIntendedUseEnum.Blunderville:
+                StopIfRunning($"Unsupported Area entered ({newTerritoryId})");
+                break;
+
+            case ETerritoryIntendedUseEnum.Dungeon:
+            case ETerritoryIntendedUseEnum.VariantDungeon:
+            case ETerritoryIntendedUseEnum.AllianceRaid:
+            case ETerritoryIntendedUseEnum.Trial:
+            case ETerritoryIntendedUseEnum.Raid:
+            case ETerritoryIntendedUseEnum.Raid2:
+            case ETerritoryIntendedUseEnum.SeasonalEvent:
+            case ETerritoryIntendedUseEnum.SeasonalEvent2:
+            case ETerritoryIntendedUseEnum.CriterionDuty:
+            case ETerritoryIntendedUseEnum.CriterionSavageDuty:
+                _uncheckedTeritoryId = newTerritoryId;
+                _logger.LogInformation("Will check territory {TerritoryId} after loading", newTerritoryId);
+                break;
+        }
+    }
+
+    public unsafe void Update()
+    {
+        if (_uncheckedTeritoryId == _clientState.TerritoryType && GameMain.Instance()->TerritoryLoadState == 2)
+        {
+            var groupManager = GroupManager.Instance();
+            if (groupManager == null)
+                return;
+
+            byte memberCount = groupManager->MainGroup.MemberCount;
+            _logger.LogDebug("Terrritory {TerritoryId} with {MemberCount} members", _uncheckedTeritoryId, memberCount);
+            if (memberCount > 1)
+                StopIfRunning("Other party members present");
+
+            _uncheckedTeritoryId = null;
+        }
+    }
+
+    private void StopIfRunning(string reason)
+    {
+        if (_questController.IsRunning || _questController.AutomationType != QuestController.EAutomationType.Manual)
+        {
+            _chatGui.PrintError(
+                $"Stopping Questionable: {reason}. If you believe this to be correct, please restart Questionable manually.",
+                CommandHandler.MessageTag, CommandHandler.TagColor);
+            _questController.Stop(reason);
+        }
+    }
+
+    public void Dispose()
+    {
+        _clientState.TerritoryChanged -= TerritoryChanged;
+    }
+
+    // from https://github.com/NightmareXIV/ECommons/blob/f69e460e95134c72592654059843b138b4c01a9e/ECommons/ExcelServices/TerritoryIntendedUseEnum.cs#L5
+    [UsedImplicitly(ImplicitUseTargetFlags.Members, Reason = "game data")]
+    private enum ETerritoryIntendedUseEnum : byte
+    {
+        CityArea = 0,
+        OpenWorld = 1,
+        Inn = 2,
+        Dungeon = 3,
+        VariantDungeon = 4,
+        Gaol = 5,
+        StartingArea = 6,
+        QuestArea = 7,
+        AllianceRaid = 8,
+        QuestBattle = 9,
+        Trial = 10,
+        QuestArea2 = 12,
+        ResidentialArea = 13,
+        HousingInstances = 14,
+        QuestArea3 = 15,
+        Raid = 16,
+        Raid2 = 17,
+        Frontline = 18,
+        ChocoboSquare = 20,
+        RestorationEvent = 21,
+        Sanctum = 22,
+        GoldSaucer = 23,
+        LordOfVerminion = 25,
+        Diadem = 26,
+        HallOfTheNovice = 27,
+        CrystallineConflict = 28,
+        QuestBattle2 = 29,
+        Barracks = 30,
+        DeepDungeon = 31,
+        SeasonalEvent = 32,
+        TreasureMapDuty = 33,
+        SeasonalEventDuty = 34,
+        Battlehall = 35,
+        CrystallineConflict2 = 37,
+        Diadem2 = 38,
+        RivalWings = 39,
+        Unknown1 = 40,
+        Eureka = 41,
+        SeasonalEvent2 = 43,
+        LeapOfFaith = 44,
+        MaskedCarnivale = 45,
+        OceanFishing = 46,
+        Diadem3 = 47,
+        Bozja = 48,
+        IslandSanctuary = 49,
+        Battlehall2 = 50,
+        Battlehall3 = 51,
+        LargeScaleRaid = 52,
+        LargeScaleSavageRaid = 53,
+        QuestArea4 = 54,
+        TribalInstance = 56,
+        CriterionDuty = 57,
+        CriterionSavageDuty = 58,
+        Blunderville = 59,
+    }
+}
index a4b228d..1c6f8a6 100644 (file)
@@ -7,6 +7,7 @@ using Dalamud.Plugin.Services;
 using Microsoft.Extensions.Logging;
 using Questionable.Controller;
 using Questionable.Controller.GameUi;
+using Questionable.Controller.Utils;
 using Questionable.Windows;
 
 namespace Questionable;
@@ -23,6 +24,7 @@ internal sealed class DalamudInitializer : IDisposable
     private readonly ConfigWindow _configWindow;
     private readonly IToastGui _toastGui;
     private readonly Configuration _configuration;
+    private readonly PartyWatchDog _partyWatchDog;
     private readonly ILogger<DalamudInitializer> _logger;
 
     public DalamudInitializer(
@@ -42,6 +44,7 @@ internal sealed class DalamudInitializer : IDisposable
         PriorityWindow priorityWindow,
         IToastGui toastGui,
         Configuration configuration,
+        PartyWatchDog partyWatchDog,
         ILogger<DalamudInitializer> logger)
     {
         _pluginInterface = pluginInterface;
@@ -54,6 +57,7 @@ internal sealed class DalamudInitializer : IDisposable
         _configWindow = configWindow;
         _toastGui = toastGui;
         _configuration = configuration;
+        _partyWatchDog = partyWatchDog;
         _logger = logger;
 
         _windowSystem.AddWindow(oneTimeSetupWindow);
@@ -77,6 +81,7 @@ internal sealed class DalamudInitializer : IDisposable
 
     private void FrameworkUpdate(IFramework framework)
     {
+        _partyWatchDog.Update();
         _questController.Update();
 
         try
index 753ddb1..04d69bc 100644 (file)
@@ -20,6 +20,7 @@ using Questionable.Controller.Steps.Common;
 using Questionable.Controller.Steps.Gathering;
 using Questionable.Controller.Steps.Interactions;
 using Questionable.Controller.Steps.Leves;
+using Questionable.Controller.Utils;
 using Questionable.Data;
 using Questionable.External;
 using Questionable.Functions;
@@ -260,6 +261,8 @@ public sealed class QuestionablePlugin : IDalamudPlugin
         serviceCollection.AddSingleton<ShopController>();
         serviceCollection.AddSingleton<InterruptHandler>();
 
+        serviceCollection.AddSingleton<PartyWatchDog>();
+
         serviceCollection.AddSingleton<CraftworksSupplyController>();
         serviceCollection.AddSingleton<CreditsController>();
         serviceCollection.AddSingleton<HelpUiController>();