Skip to content

Commit 5b1d8f8

Browse files
committed
requires further testing for #12
1 parent 43351f8 commit 5b1d8f8

File tree

5 files changed

+87
-40
lines changed

5 files changed

+87
-40
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
namespace SomethingNeedDoing.Core.Events;
2+
3+
/// <summary>
4+
/// Event arguments for function execution requests.
5+
/// </summary>
6+
/// <param name="macroId">The ID of the macro containing the function.</param>
7+
/// <param name="functionName">The name of the function to execute.</param>
8+
/// <param name="triggerArgs">The trigger event arguments that caused the function execution.</param>
9+
public class FunctionExecutionRequestedEventArgs(string macroId, string functionName, TriggerEventArgs triggerArgs) : EventArgs
10+
{
11+
/// <summary>
12+
/// Gets the ID of the macro containing the function.
13+
/// </summary>
14+
public string MacroId { get; } = macroId;
15+
16+
/// <summary>
17+
/// Gets the name of the function to execute.
18+
/// </summary>
19+
public string FunctionName { get; } = functionName;
20+
21+
/// <summary>
22+
/// Gets the trigger event arguments that caused the function execution.
23+
/// </summary>
24+
public TriggerEventArgs TriggerArgs { get; } = triggerArgs;
25+
}

SomethingNeedDoing/Core/Interfaces/IMacroScheduler.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,11 @@ public interface IMacroScheduler
129129
/// <param name="macroId">The ID of the macro to execute cleanup for.</param>
130130
/// <param name="reason">The reason for cleanup execution.</param>
131131
void ExecuteCleanup(string macroId, string reason = "Manual");
132+
133+
/// <summary>
134+
/// Gets the engine currently executing a macro.
135+
/// </summary>
136+
/// <param name="macroId">The ID of the macro.</param>
137+
/// <returns>The engine, or null if not found.</returns>
138+
IMacroEngine? GetEngineForMacro(string macroId);
132139
}

SomethingNeedDoing/LuaMacro/NLuaMacroEngine.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ public class NLuaMacroEngine(LuaModuleManager moduleManager) : IMacroEngine
2626
public IMacroScheduler? Scheduler { get; set; }
2727

2828
private readonly Dictionary<string, TemporaryMacro> _temporaryMacros = [];
29+
private readonly Dictionary<string, Lua> _activeLuaEnvironments = [];
2930

3031
/// <inheritdoc/>
3132
public IMacro? GetTemporaryMacro(string macroId) => _temporaryMacros.TryGetValue(macroId, out var macro) ? macro : null;
33+
public Lua? GetLuaEnvironment(string macroId) => _activeLuaEnvironments.TryGetValue(macroId, out var lua) ? lua : null;
3234

3335
/// <summary>
3436
/// Represents the current state of a macro execution.
@@ -91,6 +93,8 @@ private async Task ExecuteMacro(MacroInstance macro, CancellationToken externalT
9193
lua.DoString("luanet.load_assembly('FFXIVClientStructs')");
9294
moduleManager.RegisterAll(lua);
9395

96+
_activeLuaEnvironments[macro.Macro.Id] = lua; // for function triggers to access the same state
97+
9498
await LoadDependenciesIntoScope(lua, macro.Macro);
9599
await Svc.Framework.RunOnTick(async () =>
96100
{
@@ -234,6 +238,7 @@ await Svc.Framework.RunOnTick(async () =>
234238
}
235239
finally
236240
{
241+
_activeLuaEnvironments.Remove(macro.Macro.Id);
237242
macro.Dispose();
238243
}
239244
}

SomethingNeedDoing/Managers/MacroScheduler.cs

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
using Dalamud.Game.Text;
55
using Dalamud.Game.Text.SeStringHandling;
66
using Dalamud.Plugin.Services;
7+
using NLua;
78
using SomethingNeedDoing.Core.Events;
89
using SomethingNeedDoing.Core.Interfaces;
910
using SomethingNeedDoing.LuaMacro;
1011
using SomethingNeedDoing.LuaMacro.Wrappers;
1112
using SomethingNeedDoing.NativeMacro;
12-
using System.Collections.Concurrent;
1313
using System.Text.RegularExpressions;
1414
using System.Threading;
1515
using System.Threading.Tasks;
@@ -20,8 +20,8 @@ namespace SomethingNeedDoing.Managers;
2020
/// </summary>
2121
public class MacroScheduler : IMacroScheduler, IDisposable
2222
{
23-
private readonly ConcurrentDictionary<string, IMacroEngine> _enginesByMacroId = [];
24-
private readonly ConcurrentDictionary<string, MacroExecutionState> _macroStates = [];
23+
private readonly Dictionary<string, MacroExecutionState> _macroStates = [];
24+
private readonly Dictionary<string, IMacroEngine> _enginesByMacroId = [];
2525
private readonly Dictionary<string, AutoRetainerApi> _arApis = [];
2626
private readonly Dictionary<string, AddonEventConfig> _addonEvents = [];
2727
private readonly MacroHierarchyManager _macroHierarchy = new();
@@ -54,6 +54,7 @@ public MacroScheduler(NativeMacroEngine nativeEngine, NLuaMacroEngine luaEngine,
5454
_nativeEngine.MacroError += OnEngineError;
5555
_luaEngine.MacroError += OnEngineError;
5656
_triggerEventManager.TriggerEventOccurred += OnTriggerEventOccurred;
57+
_triggerEventManager.FunctionExecutionRequested += OnFunctionExecutionRequested;
5758

5859
_nativeEngine.MacroControlRequested += OnMacroControlRequested;
5960
_luaEngine.MacroControlRequested += OnMacroControlRequested;
@@ -355,7 +356,7 @@ public async void StopMacro(string macroId)
355356
/// <param name="macroId">The ID of the macro to clean up.</param>
356357
public void CleanupMacro(string macroId)
357358
{
358-
if (_macroStates.TryRemove(macroId, out var state))
359+
if (_macroStates.Remove(macroId, out var state))
359360
{
360361
if (state.Macro is ConfigMacro configMacro)
361362
_triggerEventManager.UnregisterAllTriggers(configMacro);
@@ -366,7 +367,7 @@ public void CleanupMacro(string macroId)
366367
state.Macro.StateChanged -= OnMacroStateChanged;
367368
}
368369

369-
_enginesByMacroId.TryRemove(macroId, out _);
370+
_enginesByMacroId.Remove(macroId);
370371
}
371372

372373
/// <inheritdoc/>
@@ -551,15 +552,15 @@ private void OnMacroStateChanged(object? sender, MacroStateChangedEventArgs e)
551552
tempMacro.StateChanged -= OnMacroStateChanged;
552553
}
553554

554-
if (_macroStates.TryRemove(e.MacroId, out var state))
555+
if (_macroStates.Remove(e.MacroId, out var state))
555556
{
556557
UnregisterFunctionTriggers(state.Macro);
557558
state.CancellationSource.Cancel();
558559
state.CancellationSource.Dispose();
559560
state.Macro.StateChanged -= OnMacroStateChanged;
560561
}
561562

562-
_enginesByMacroId.TryRemove(e.MacroId, out _);
563+
_enginesByMacroId.Remove(e.MacroId);
563564
}
564565
}
565566

@@ -583,19 +584,39 @@ private void OnTriggerEventOccurred(object? sender, TriggerEventArgs e)
583584

584585
_ = StartMacro(macro, e);
585586
}
586-
else
587-
Svc.Log.Warning($"[{nameof(MacroScheduler)}] Could not find parent macro {parts[0]} for temporary macro {macro.Id}");
588587
}
589588
else
590589
{
591-
// don't let an infinte loop of it starting itself happen
592-
if (macro.State == MacroState.Running)
590+
_ = StartMacro(macro, e);
591+
}
592+
}
593+
}
594+
595+
/// <summary>
596+
/// Handles function execution requests from trigger events.
597+
/// </summary>
598+
/// <param name="sender">The sender of the event.</param>
599+
/// <param name="e">The function execution request event arguments.</param>
600+
private void OnFunctionExecutionRequested(object? sender, FunctionExecutionRequestedEventArgs e)
601+
{
602+
try
603+
{
604+
if (GetEngineForMacro(e.MacroId) is NLuaMacroEngine nluaEngine && nluaEngine.GetLuaEnvironment(e.MacroId) is Lua lua)
605+
{
606+
if (C.GetMacro(e.MacroId) is { State: MacroState.Running } macro)
593607
{
594-
Svc.Log.Verbose($"[{nameof(MacroScheduler)}] Macro {macro.Id} is already running, skipping trigger");
595-
return;
608+
Svc.Log.Verbose($"Executing function {e.FunctionName} in macro {macro.Name}");
609+
lua.DoString($"{e.FunctionName}()"); // call in the parent's lua state
596610
}
597-
_ = StartMacro(macro, e);
611+
else
612+
Svc.Log.Debug($"Skipping function {e.FunctionName} for stopped macro {e.MacroId}");
598613
}
614+
else
615+
Svc.Log.Warning($"Could not find active Lua environment for macro {e.MacroId}");
616+
}
617+
catch (Exception ex)
618+
{
619+
Svc.Log.Error($"Error executing function {e.FunctionName} for macro {e.MacroId}: {ex}");
599620
}
600621
}
601622

@@ -795,4 +816,7 @@ public void Dispose()
795816

796817
/// <inheritdoc/>
797818
public void ExecuteCleanup(string macroId, string reason = "Manual") => _cleanupManager.ExecuteCleanup(macroId, reason);
819+
820+
/// <inheritdoc/>
821+
public IMacroEngine? GetEngineForMacro(string macroId) => _enginesByMacroId.TryGetValue(macroId, out var engine) ? engine : null;
798822
}

SomethingNeedDoing/Managers/TriggerEventManager.cs

Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using SomethingNeedDoing.Core.Events;
22
using SomethingNeedDoing.Core.Interfaces;
33
using System.Threading.Tasks;
4-
using System.Text.RegularExpressions;
54

65
namespace SomethingNeedDoing.Managers;
76

@@ -65,6 +64,11 @@ public class TriggerEventManager : IDisposable
6564
/// </summary>
6665
public event EventHandler<TriggerEventArgs>? TriggerEventOccurred;
6766

67+
/// <summary>
68+
/// Event raised when a function execution is requested.
69+
/// </summary>
70+
public event EventHandler<FunctionExecutionRequestedEventArgs>? FunctionExecutionRequested;
71+
6872
/// <summary>
6973
/// Registers a macro to handle a specific trigger event.
7074
/// </summary>
@@ -223,43 +227,25 @@ public async Task RaiseTriggerEvent(TriggerEvent eventType, object? data = null)
223227
if (string.IsNullOrEmpty(triggerFunction.FunctionName))
224228
{
225229
// Macro-level trigger: raise the event for the entire macro
226-
Svc.Log.Verbose($"Raising trigger event {eventType} for macro {triggerFunction.Macro.Name}");
230+
Svc.Log.Verbose($"[{nameof(TriggerEventManager)}] Raising trigger event {eventType} for macro {triggerFunction.Macro.Name}");
227231
TriggerEventOccurred?.Invoke(triggerFunction.Macro, args);
228232
}
229233
else
230234
{
231-
string functionContent;
235+
// For function-level triggers, request function execution via event (doing this to avoid circular dependencies)
232236
if (triggerFunction.Macro.Type == MacroType.Lua)
233237
{
234-
Svc.Log.Verbose($"Looking for function {triggerFunction.FunctionName} in macro {triggerFunction.Macro.Name}");
235-
236-
// get only the function body between 'function ...' and the matching 'end', nothing after
237-
var match = Regex.Match(triggerFunction.Macro.Content, $@"function\s+{triggerFunction.FunctionName}\s*\([^)]*\)\s*\n(.*?)\n\s*end", RegexOptions.Singleline | RegexOptions.IgnoreCase);
238-
if (!match.Success)
239-
{
240-
Svc.Log.Error($"Could not find function {triggerFunction.FunctionName} in macro {triggerFunction.Macro.Name}");
241-
continue;
242-
}
243-
var functionBody = match.Groups[1].Value.Trim();
244-
functionContent = functionBody;
238+
Svc.Log.Verbose($"[{nameof(TriggerEventManager)}] Requesting function execution for {triggerFunction.FunctionName} in macro {triggerFunction.Macro.Name}");
239+
FunctionExecutionRequested?.Invoke(this, new FunctionExecutionRequestedEventArgs(triggerFunction.Macro.Id, triggerFunction.FunctionName, args));
245240
}
246241
else
247-
functionContent = triggerFunction.Macro.Content; // natives just use the whole macro
248-
249-
// Create a temporary macro with the parent macro's ID
250-
var tempMacroId = $"{triggerFunction.Macro.Id}_{triggerFunction.FunctionName}_{Guid.NewGuid()}";
251-
var tempMacro = new TemporaryMacro(functionContent, tempMacroId)
252-
{
253-
Name = $"{triggerFunction.Macro.Name} - {triggerFunction.FunctionName}",
254-
Type = triggerFunction.Macro.Type
255-
};
256-
Svc.Log.Verbose($"Created temporary macro {tempMacro.Id} for function {triggerFunction.FunctionName} in macro {triggerFunction.Macro.Name}");
257-
TriggerEventOccurred?.Invoke(tempMacro, args);
242+
// I could technically support this, but I don't think it's necessary
243+
throw new NotSupportedException($"[{nameof(TriggerEventManager)}] Trigger event handling for {triggerFunction.Macro.Type} macros is not supported.");
258244
}
259245
}
260246
catch (Exception ex)
261247
{
262-
Svc.Log.Error($"Error handling trigger event {eventType} for macro {triggerFunction.Macro.Name} function {triggerFunction.FunctionName}: {ex}");
248+
Svc.Log.Error($"[{nameof(TriggerEventManager)}] Error handling trigger event {eventType} for macro {triggerFunction.Macro.Name} function {triggerFunction.FunctionName}: {ex}");
263249
}
264250
}
265251
}

0 commit comments

Comments
 (0)