Skip to content

Commit 71cb552

Browse files
authored
Merge pull request #58 from ELDment/beta-2
2 parents 5a24780 + 9e9e514 commit 71cb552

File tree

9 files changed

+287
-140
lines changed

9 files changed

+287
-140
lines changed

managed/src/SwiftlyS2.Core/Modules/Engine/CommandTrackerManager.cs

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
using System.Text;
22
using System.Collections.Concurrent;
33
using Spectre.Console;
4-
using SwiftlyS2.Shared;
54
using SwiftlyS2.Shared.Misc;
65
using SwiftlyS2.Shared.Events;
7-
using SwiftlyS2.Shared.Services;
86

97
namespace SwiftlyS2.Core.Services;
108

@@ -27,25 +25,20 @@ private readonly record struct ExecutingCommand(Action<string> Callback)
2725
private readonly CancellationTokenSource cancellationTokenSource = new();
2826
private readonly ConcurrentQueue<Action<string>> pendingCallbacks = new();
2927
private volatile bool disposed;
30-
private volatile bool eventsSubscribed;
3128

3229
public CommandTrackerManager()
3330
{
34-
eventsSubscribed = false;
35-
3631
StartCleanupTimer();
3732
}
3833

3934
public void ProcessCommand(IOnCommandExecuteHookEvent @event)
4035
{
41-
if (string.IsNullOrEmpty(@event.OriginalName) || !@event.OriginalName.StartsWith("^wb^"))
42-
{
43-
Interlocked.Exchange(ref currentCommandContainer, CommandIdContainer.Empty);
44-
return;
45-
}
46-
4736
if (@event.HookMode == HookMode.Pre)
4837
{
38+
if (string.IsNullOrWhiteSpace(@event.Command[0]) || !@event.Command[0]!.StartsWith("^wb^"))
39+
{
40+
return;
41+
}
4942
ProcessCommandStart(@event);
5043
}
5144
else if (@event.HookMode == HookMode.Post)
@@ -78,7 +71,7 @@ public void ProcessCommandStart(IOnCommandExecuteHookEvent @event)
7871
{
7972
var newContainer = new CommandIdContainer(newCommandId);
8073
Interlocked.Exchange(ref currentCommandContainer, newContainer);
81-
@event.SetCommandName(@event.OriginalName.Replace("^wb^", string.Empty));
74+
@event.Command.Tokenize($"{@event.Command[0]!.Replace("^wb^", string.Empty)} {@event.Command.ArgS}");
8275
}
8376
}
8477
else
@@ -87,7 +80,7 @@ public void ProcessCommandStart(IOnCommandExecuteHookEvent @event)
8780
}
8881
}
8982

90-
public void ProcessCommandEnd(IOnCommandExecuteHookEvent @event)
83+
public void ProcessCommandEnd(IOnCommandExecuteHookEvent _)
9184
{
9285
var previousContainer = Interlocked.Exchange(ref currentCommandContainer, CommandIdContainer.Empty);
9386
var commandId = previousContainer?.Value ?? Guid.Empty;
@@ -101,10 +94,7 @@ public void ProcessCommandEnd(IOnCommandExecuteHookEvent @event)
10194
output.Append(line);
10295
}
10396

104-
Task.Run(() =>
105-
{
106-
command.Callback.Invoke(output.ToString());
107-
});
97+
Task.Run(() => command.Callback.Invoke(output.ToString()));
10898
}
10999
}
110100

@@ -119,7 +109,8 @@ private void StartCleanupTimer()
119109
await Task.Delay(TimeSpan.FromMilliseconds(200), cancellationTokenSource.Token);
120110
CleanupExpiredCommands();
121111
}
122-
catch (Exception ex) {
112+
catch (Exception ex)
113+
{
123114
AnsiConsole.WriteException(ex);
124115
}
125116
}
Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
using SwiftlyS2.Shared.Misc;
22
using SwiftlyS2.Shared.Events;
3+
using SwiftlyS2.Shared.Natives;
34

45
namespace SwiftlyS2.Core.Events;
56

67
internal class OnCommandExecuteHookEvent : IOnCommandExecuteHookEvent
78
{
8-
public required string OriginalName { get; init; }
9-
public required string[] OriginalArgs { get; init; }
10-
public string CommandName { get; set; } = string.Empty;
9+
private CCommand _command;
1110

12-
public required HookMode HookMode { get; init; }
11+
public ref CCommand Command => ref _command;
1312

14-
public bool Intercepted { get; set; } = false;
13+
public HookMode HookMode { get; init; }
1514

16-
public void SetCommandName(string name) {
17-
if (HookMode == HookMode.Post) return;
18-
CommandName = name;
19-
Intercepted = true;
15+
public OnCommandExecuteHookEvent(ref CCommand command, HookMode hookMode)
16+
{
17+
_command = command;
18+
HookMode = hookMode;
2019
}
2120
}

managed/src/SwiftlyS2.Core/Services/CoreHookService.cs

Lines changed: 13 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using SwiftlyS2.Shared.Memory;
1010
using SwiftlyS2.Shared.Misc;
1111
using SwiftlyS2.Shared.SchemaDefinitions;
12+
using SwiftlyS2.Shared.Natives;
1213

1314
namespace SwiftlyS2.Core.Services;
1415

@@ -103,66 +104,28 @@ private void HookCommandExecute()
103104
var address = _Core.GameData.GetSignature("Cmd_ExecuteCommand");
104105

105106
_Logger.LogInformation("Hooking Cmd_ExecuteCommand at {Address}", address);
106-
var commandNameOffset = NativeOffsets.Fetch("CommandNameOffset");
107-
var commandArgsOffset = NativeOffsets.Fetch("CommandArgsOffset");
108107

109108
_ExecuteCommand = _Core.Memory.GetUnmanagedFunctionByAddress<ExecuteCommandDelegate>(address);
110109
_ExecuteCommandGuid = _ExecuteCommand.AddHook((next) =>
111110
{
112111
return (a1, a2, a3, a4, a5) =>
113112
{
114-
var commandName = (a5 != nint.Zero && a5 < nint.MaxValue && commandNameOffset != 0) switch
113+
unsafe
115114
{
116-
true when Marshal.ReadIntPtr(new nint(a5 + commandNameOffset)) is var basePtr && basePtr != nint.Zero && basePtr < nint.MaxValue
117-
=> Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(basePtr)) ?? string.Empty,
118-
_ => string.Empty
119-
};
120-
var commandArgs = (a5 != nint.Zero && a5 < nint.MaxValue && commandArgsOffset != 0) switch
121-
{
122-
true => Marshal.PtrToStringAnsi(new nint(a5 + commandArgsOffset)) ?? string.Empty,
123-
_ => string.Empty
124-
};
125-
126-
var argsSplit = commandArgs.Split(' ', StringSplitOptions.RemoveEmptyEntries);
127-
var args = argsSplit.Length > 1 ? argsSplit[1..] : [];
128-
129-
var preEvent = new OnCommandExecuteHookEvent
130-
{
131-
OriginalName = commandName,
132-
OriginalArgs = args,
133-
HookMode = HookMode.Pre
134-
};
135-
EventPublisher.InvokeOnCommandExecuteHook(preEvent);
136-
137-
nint newCommandNamePtr = nint.Zero;
138-
139-
if (preEvent.Intercepted && preEvent.CommandName.Length < commandName.Length)
140-
{
141-
var newCommandName = Encoding.UTF8.GetBytes(preEvent.CommandName);
142-
143-
newCommandNamePtr = NativeAllocator.Alloc((ulong)(newCommandName.Length + 1));
144-
newCommandNamePtr.Write(newCommandName.Length, 0);
145-
146-
newCommandNamePtr.CopyFrom(newCommandName);
147-
(a5 + commandNameOffset).Read<nint>().Write(newCommandNamePtr);
148-
}
149-
150-
var result = next()(a1, a2, a3, a4, a5);
115+
if (a5 != nint.Zero)
116+
{
117+
ref var command = ref Unsafe.AsRef<CCommand>((void*)a5);
118+
var @eventPre = new OnCommandExecuteHookEvent(ref command, HookMode.Pre);
119+
EventPublisher.InvokeOnCommandExecuteHook(@eventPre);
151120

152-
var postEvent = new OnCommandExecuteHookEvent
153-
{
154-
OriginalName = commandName,
155-
OriginalArgs = args,
156-
HookMode = HookMode.Post
157-
};
158-
EventPublisher.InvokeOnCommandExecuteHook(postEvent);
121+
var result = next()(a1, a2, a3, a4, a5);
159122

160-
if (newCommandNamePtr != nint.Zero)
161-
{
162-
NativeAllocator.Free(newCommandNamePtr);
123+
var @eventPost = new OnCommandExecuteHookEvent(ref command, HookMode.Post);
124+
EventPublisher.InvokeOnCommandExecuteHook(@eventPost);
125+
return result;
126+
}
127+
return next()(a1, a2, a3, a4, a5);
163128
}
164-
165-
return result;
166129
};
167130
});
168131
}
Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,20 @@
11
using SwiftlyS2.Shared.Misc;
2+
using SwiftlyS2.Shared.Natives;
23

34
namespace SwiftlyS2.Shared.Events;
45

56
/// <summary>
67
/// Called when a command is executed.
78
/// </summary>
8-
public interface IOnCommandExecuteHookEvent {
9-
10-
/// <summary>
11-
/// The original command name.
12-
/// </summary>
13-
public string OriginalName { get; }
14-
9+
public interface IOnCommandExecuteHookEvent
10+
{
1511
/// <summary>
16-
/// The original command arguments.
12+
/// The command.
1713
/// </summary>
18-
public string[] OriginalArgs { get; }
14+
public ref CCommand Command { get; }
1915

2016
/// <summary>
21-
/// The command arguments.
17+
/// The hook mode.
2218
/// </summary>
2319
public HookMode HookMode { get; }
24-
25-
/// <summary>
26-
/// Intercept and modify the command name.
27-
/// This will modify the command name and stop the following hooks and original function.
28-
/// </summary>
29-
/// <param name="name">The name to modify.</param>
30-
public void SetCommandName(string name);
3120
}

0 commit comments

Comments
 (0)