Skip to content

Commit 5d6c10d

Browse files
committed
add cleanup event
1 parent 282a273 commit 5d6c10d

File tree

8 files changed

+206
-6
lines changed

8 files changed

+206
-6
lines changed

SomethingNeedDoing/Core/Interfaces/IMacroScheduler.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,18 @@ public interface IMacroScheduler
115115
/// <param name="macro">The macro to unsubscribe.</param>
116116
/// <param name="triggerEvent">The trigger event to unsubscribe from.</param>
117117
void UnsubscribeFromTriggerEvent(IMacro macro, TriggerEvent triggerEvent);
118+
119+
/// <summary>
120+
/// Checks if a macro has any registered cleanup functions.
121+
/// </summary>
122+
/// <param name="macroId">The ID of the macro to check.</param>
123+
/// <returns>True if the macro has cleanup functions, false otherwise.</returns>
124+
bool HasCleanupFunctions(string macroId);
125+
126+
/// <summary>
127+
/// Manually executes cleanup functions for a macro.
128+
/// </summary>
129+
/// <param name="macroId">The ID of the macro to execute cleanup for.</param>
130+
/// <param name="reason">The reason for cleanup execution.</param>
131+
void ExecuteCleanup(string macroId, string reason = "Manual");
118132
}

SomethingNeedDoing/Gui/ChangelogWindow.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ private void AddGeneralChangelogs()
6262
Add("12.11", "Fixed /loop");
6363
Add("12.11", "Fixed craft skip trigger on non craft actions");
6464
Add("12.13", "Added imports for all enums, now callable via luanet.enum");
65+
Add("12.14", "Added OnCleanup function for lua scripts.");
6566
}
6667

6768
private void Add(string version, string description)

SomethingNeedDoing/Gui/StatusWindow.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using Dalamud.Interface.Windowing;
55
using ECommons.ImGuiMethods;
66
using SomethingNeedDoing.Core.Interfaces;
7-
using SomethingNeedDoing.Scheduler;
7+
using SomethingNeedDoing.Managers;
88

99
namespace SomethingNeedDoing.Gui;
1010

SomethingNeedDoing/Gui/Tabs/HelpGeneralTab.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using Dalamud.Interface.Colors;
22
using Dalamud.Interface.Utility.Raii;
33
using ECommons.ImGuiMethods;
4-
using SomethingNeedDoing.Scheduler;
4+
using SomethingNeedDoing.Managers;
55

66
namespace SomethingNeedDoing.Gui.Tabs;
77
public static class HelpGeneralTab
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
using SomethingNeedDoing.Core.Interfaces;
2+
using System.Text.RegularExpressions;
3+
4+
namespace SomethingNeedDoing.Managers;
5+
6+
/// <summary>
7+
/// Manages cleanup functions for macros when they stop execution.
8+
/// </summary>
9+
public class CleanupManager : IDisposable
10+
{
11+
private readonly Dictionary<string, List<string>> _cleanupFunctionsByMacroId = [];
12+
private readonly Dictionary<string, IMacro> _macrosById = [];
13+
14+
/// <summary>
15+
/// Event raised when a cleanup function should be executed.
16+
/// </summary>
17+
public event EventHandler<CleanupFunctionEventArgs>? CleanupFunctionRequested;
18+
19+
/// <summary>
20+
/// Registers cleanup functions for a macro.
21+
/// </summary>
22+
public void RegisterCleanupFunctions(IMacro macro)
23+
{
24+
if (macro is TemporaryMacro) return;
25+
26+
var cleanupFunctions = new List<string>();
27+
28+
if (macro.Type == MacroType.Lua)
29+
{
30+
var patterns = new[]
31+
{
32+
@"function\s+(OnCleanup|OnDispose|OnStop|OnEnd)\s*\(",
33+
@"function\s+(Cleanup|Dispose|Stop|End)\s*\("
34+
};
35+
36+
foreach (var pattern in patterns)
37+
{
38+
var matches = Regex.Matches(macro.Content, pattern, RegexOptions.IgnoreCase);
39+
foreach (Match match in matches)
40+
{
41+
var functionName = match.Groups[1].Value;
42+
cleanupFunctions.Add(functionName);
43+
Svc.Log.Debug($"[{nameof(CleanupManager)}] Found cleanup function {functionName} in macro {macro.Name}");
44+
}
45+
}
46+
}
47+
48+
if (cleanupFunctions.Count > 0)
49+
{
50+
_cleanupFunctionsByMacroId[macro.Id] = cleanupFunctions;
51+
_macrosById[macro.Id] = macro;
52+
Svc.Log.Debug($"[{nameof(CleanupManager)}] Registered {cleanupFunctions.Count} cleanup functions for macro {macro.Name}");
53+
}
54+
}
55+
56+
/// <summary>
57+
/// Unregisters cleanup functions for a macro.
58+
/// </summary>
59+
public void UnregisterCleanupFunctions(IMacro macro)
60+
{
61+
if (macro is TemporaryMacro) return;
62+
63+
if (_cleanupFunctionsByMacroId.Remove(macro.Id))
64+
{
65+
_macrosById.Remove(macro.Id);
66+
Svc.Log.Debug($"[{nameof(CleanupManager)}] Unregistered cleanup functions for macro {macro.Name}");
67+
}
68+
}
69+
70+
/// <summary>
71+
/// Executes cleanup functions for a macro.
72+
/// </summary>
73+
public void ExecuteCleanup(string macroId, string reason = "Stopped")
74+
{
75+
if (!_cleanupFunctionsByMacroId.TryGetValue(macroId, out var cleanupFunctions) || !_macrosById.TryGetValue(macroId, out var macro))
76+
return;
77+
78+
Svc.Log.Info($"[{nameof(CleanupManager)}] Executing {cleanupFunctions.Count} cleanup functions for macro {macro.Name} (reason: {reason})");
79+
80+
foreach (var functionName in cleanupFunctions)
81+
{
82+
try
83+
{
84+
string functionContent;
85+
if (macro.Type == MacroType.Lua)
86+
{
87+
var match = Regex.Match(macro.Content, $@"function\s+{functionName}\s*\([^)]*\)\s*\n(.*?)\n\s*end", RegexOptions.Singleline | RegexOptions.IgnoreCase);
88+
if (!match.Success)
89+
{
90+
Svc.Log.Warning($"[{nameof(CleanupManager)}] Could not find function {functionName} in macro {macro.Name}");
91+
continue;
92+
}
93+
functionContent = match.Groups[1].Value.Trim();
94+
}
95+
else
96+
throw new NotSupportedException($"Cleanup functions are not supported for macro type {macro.Type}");
97+
98+
var tempMacroId = $"{macro.Id}_cleanup_{functionName}_{Guid.NewGuid()}";
99+
var tempMacro = new TemporaryMacro(functionContent, tempMacroId)
100+
{
101+
Name = $"{macro.Name} - {functionName} (Cleanup)",
102+
Type = macro.Type
103+
};
104+
105+
Svc.Log.Debug($"[{nameof(CleanupManager)}] Created cleanup temporary macro {tempMacro.Id} for function {functionName}");
106+
CleanupFunctionRequested?.Invoke(this, new CleanupFunctionEventArgs(tempMacro, functionName, reason));
107+
}
108+
catch (Exception ex)
109+
{
110+
Svc.Log.Error(ex, $"[{nameof(CleanupManager)}] Error executing cleanup function {functionName} for macro {macro.Name}");
111+
}
112+
}
113+
}
114+
115+
/// <summary>
116+
/// Gets all registered cleanup functions for a macro.
117+
/// </summary>
118+
public IReadOnlyList<string> GetCleanupFunctions(string macroId)
119+
=> _cleanupFunctionsByMacroId.TryGetValue(macroId, out var functions) ? functions.AsReadOnly() : Array.Empty<string>();
120+
121+
/// <summary>
122+
/// Checks if a macro has any registered cleanup functions.
123+
/// </summary>
124+
public bool HasCleanupFunctions(string macroId) => _cleanupFunctionsByMacroId.ContainsKey(macroId);
125+
126+
/// <summary>
127+
/// Disposes of the cleanup manager.
128+
/// </summary>
129+
public void Dispose()
130+
{
131+
_cleanupFunctionsByMacroId.Clear();
132+
_macrosById.Clear();
133+
}
134+
}
135+
136+
/// <summary>
137+
/// Event arguments for cleanup function execution.
138+
/// </summary>
139+
/// <param name="tempMacro">The temporary macro containing the cleanup function.</param>
140+
/// <param name="functionName">The name of the cleanup function.</param>
141+
/// <param name="reason">The reason for cleanup execution.</param>
142+
public class CleanupFunctionEventArgs(IMacro tempMacro, string functionName, string reason) : EventArgs
143+
{
144+
/// <summary>
145+
/// Gets the temporary macro containing the cleanup function.
146+
/// </summary>
147+
public IMacro TempMacro { get; } = tempMacro;
148+
149+
/// <summary>
150+
/// Gets the name of the cleanup function.
151+
/// </summary>
152+
public string FunctionName { get; } = functionName;
153+
154+
/// <summary>
155+
/// Gets the reason for cleanup execution.
156+
/// </summary>
157+
public string Reason { get; } = reason;
158+
}

SomethingNeedDoing/Managers/MacroHierarchyManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.Collections.Concurrent;
33
using System.Threading.Tasks;
44

5-
namespace SomethingNeedDoing.Scheduler;
5+
namespace SomethingNeedDoing.Managers;
66

77
/// <summary>
88
/// Manages the hierarchy and relationships between macros and their temporary children.

SomethingNeedDoing/Managers/MacroScheduler.cs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
using System.Threading;
1515
using System.Threading.Tasks;
1616

17-
namespace SomethingNeedDoing.Scheduler;
17+
namespace SomethingNeedDoing.Managers;
1818
/// <summary>
1919
/// Manages and coordinates execution of multiple macros.
2020
/// </summary>
@@ -26,12 +26,13 @@ public class MacroScheduler : IMacroScheduler, IDisposable
2626
private readonly Dictionary<string, AddonEventConfig> _addonEvents = [];
2727
private readonly MacroHierarchyManager _macroHierarchy = new();
2828
private readonly Dictionary<string, IDisableable> _disableablePlugins = [];
29+
private readonly CleanupManager _cleanupManager = new();
2930

3031
private readonly NativeMacroEngine _nativeEngine;
3132
private readonly NLuaMacroEngine _luaEngine;
3233
private readonly TriggerEventManager _triggerEventManager;
3334

34-
private readonly HashSet<string> _functionTriggersRegistered = new();
35+
private readonly HashSet<string> _functionTriggersRegistered = [];
3536

3637
/// <inheritdoc/>
3738
public event EventHandler<MacroStateChangedEventArgs>? MacroStateChanged;
@@ -59,6 +60,8 @@ public MacroScheduler(NativeMacroEngine nativeEngine, NLuaMacroEngine luaEngine,
5960
_nativeEngine.MacroStepCompleted += OnMacroStepCompleted;
6061
_luaEngine.MacroStepCompleted += OnMacroStepCompleted;
6162

63+
_cleanupManager.CleanupFunctionRequested += OnCleanupFunctionRequested;
64+
6265
foreach (var plugin in disableablePlugins)
6366
_disableablePlugins[plugin.InternalName] = plugin;
6467

@@ -165,6 +168,7 @@ private void RegisterFunctionTriggers(IMacro macro)
165168
}
166169
}
167170
_functionTriggersRegistered.Add(macro.Id);
171+
_cleanupManager.RegisterCleanupFunctions(macro);
168172
}
169173

170174
/// <summary>
@@ -196,6 +200,7 @@ private void UnregisterFunctionTriggers(IMacro macro)
196200
}
197201
}
198202
_functionTriggersRegistered.Remove(macro.Id);
203+
_cleanupManager.UnregisterCleanupFunctions(macro);
199204
}
200205

201206
/// <inheritdoc/>
@@ -328,6 +333,9 @@ public async void StopMacro(string macroId)
328333
UnregisterFunctionTriggers(state.Macro);
329334
await SetPluginStates(state.Macro, true);
330335

336+
// Execute cleanup functions
337+
_cleanupManager.ExecuteCleanup(macroId, "Stopped");
338+
331339
if (C.PropagateControlsToChildren)
332340
foreach (var child in _macroHierarchy.GetChildMacros(macroId).ToList())
333341
StopMacro(child.Id);
@@ -461,6 +469,8 @@ private void OnMacroStateChanged(object? sender, MacroStateChangedEventArgs e)
461469

462470
if (e.NewState is MacroState.Completed or MacroState.Error)
463471
{
472+
_cleanupManager.ExecuteCleanup(e.MacroId, e.NewState.ToString());
473+
464474
// If this is a temporary macro, unregister it and clean up
465475
if (parts.Length >= 2 && C.GetMacro(parts[0]) is { } parentMacro2)
466476
{
@@ -650,6 +660,14 @@ private void OnMacroControlRequested(object? sender, MacroControlEventArgs e)
650660

651661
private void OnMacroStepCompleted(object? sender, MacroStepCompletedEventArgs e)
652662
=> Svc.Log.Verbose($"Macro step completed for {e.MacroId}: {e.StepIndex}/{e.TotalSteps}");
663+
664+
private void OnCleanupFunctionRequested(object? sender, CleanupFunctionEventArgs e)
665+
{
666+
Svc.Log.Verbose($"[{nameof(MacroScheduler)}] Executing cleanup function {e.FunctionName} for macro {e.TempMacro.Name} (reason: {e.Reason})");
667+
668+
// Start the cleanup temporary macro
669+
_ = StartMacro(e.TempMacro);
670+
}
653671
#endregion
654672

655673
/// <inheritdoc/>
@@ -664,6 +682,8 @@ public void Dispose()
664682
_nativeEngine.MacroStepCompleted -= OnMacroStepCompleted;
665683
_luaEngine.MacroStepCompleted -= OnMacroStepCompleted;
666684

685+
_cleanupManager.CleanupFunctionRequested -= OnCleanupFunctionRequested;
686+
667687
_macroStates.Values.Each(s => s.Dispose());
668688
_macroStates.Clear();
669689
_enginesByMacroId.Clear();
@@ -683,5 +703,12 @@ public void Dispose()
683703
_addonEvents.Clear();
684704

685705
_triggerEventManager.Dispose();
706+
_cleanupManager.Dispose();
686707
}
708+
709+
/// <inheritdoc/>
710+
public bool HasCleanupFunctions(string macroId) => _cleanupManager.HasCleanupFunctions(macroId);
711+
712+
/// <inheritdoc/>
713+
public void ExecuteCleanup(string macroId, string reason = "Manual") => _cleanupManager.ExecuteCleanup(macroId, reason);
687714
}

SomethingNeedDoing/Managers/TriggerEventManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.Threading.Tasks;
44
using System.Text.RegularExpressions;
55

6-
namespace SomethingNeedDoing.Scheduler;
6+
namespace SomethingNeedDoing.Managers;
77

88
/// <summary>
99
/// Represents a function that can be triggered by an event.

0 commit comments

Comments
 (0)