Files
cs2-sendproxy/examples/CounterStrikeSharp/SendProxyNative.cs
2026-08-10 17:31:33 +02:00

129 lines
5.3 KiB
C#

using System;
using System.Buffers.Binary;
using System.Runtime.InteropServices;
namespace SendProxyInterop;
public sealed class SendProxyNative : IDisposable
{
private const string RelativeLibraryPath = "addons/sendproxy/bin/linuxsteamrt64/sendproxy.so";
private readonly IntPtr _library;
private readonly SetOverrideDelegate _setOverride;
private readonly SetVectorFirstFromFieldDelegate _setVectorFirstFromField;
private readonly RemoveOverrideDelegate _removeOverride;
private readonly ClearOverridesDelegate _clearOverrides;
private readonly MarkDirtyDelegate _markDirty;
public SendProxyNative(string libraryPath)
{
_library = NativeLibrary.Load(libraryPath);
_setOverride = GetExport<SetOverrideDelegate>("SendProxy_SetOverride");
_setVectorFirstFromField = GetExport<SetVectorFirstFromFieldDelegate>("SendProxy_SetVectorFirstFromField");
_removeOverride = GetExport<RemoveOverrideDelegate>("SendProxy_RemoveOverride");
_clearOverrides = GetExport<ClearOverridesDelegate>("SendProxy_ClearOverrides");
_markDirty = GetExport<MarkDirtyDelegate>("SendProxy_MarkDirty");
}
public static SendProxyNative FromGameDirectory(string gameDirectory)
{
string csgoDirectory = Path.GetFileName(gameDirectory).Equals("csgo", StringComparison.OrdinalIgnoreCase)
? gameDirectory
: Path.Combine(gameDirectory, "csgo");
string libraryPath = Path.Combine(csgoDirectory, RelativeLibraryPath);
if (!File.Exists(libraryPath))
throw new FileNotFoundException("SendProxy Metamod addon was not found. Install it under game/csgo/addons/sendproxy.", libraryPath);
return new SendProxyNative(libraryPath);
}
public int SetBytes(int recipientSlot, int entityIndex, string className, string fieldPath, ReadOnlySpan<byte> value)
{
byte[] bytes = value.ToArray();
return _setOverride(recipientSlot, entityIndex, className, fieldPath, bytes, bytes.Length);
}
public int SetBool(int recipientSlot, int entityIndex, string className, string fieldPath, bool value)
{
Span<byte> bytes = stackalloc byte[1];
bytes[0] = value ? (byte)1 : (byte)0;
return SetBytes(recipientSlot, entityIndex, className, fieldPath, bytes);
}
public int SetUInt32(int recipientSlot, int entityIndex, string className, string fieldPath, uint value)
{
Span<byte> bytes = stackalloc byte[4];
BinaryPrimitives.WriteUInt32LittleEndian(bytes, value);
return SetBytes(recipientSlot, entityIndex, className, fieldPath, bytes);
}
public int SetInt32(int recipientSlot, int entityIndex, string className, string fieldPath, int value)
{
Span<byte> bytes = stackalloc byte[4];
BinaryPrimitives.WriteInt32LittleEndian(bytes, value);
return SetBytes(recipientSlot, entityIndex, className, fieldPath, bytes);
}
public int SetFloat(int recipientSlot, int entityIndex, string className, string fieldPath, float value)
{
Span<byte> bytes = stackalloc byte[4];
BinaryPrimitives.WriteInt32LittleEndian(bytes, BitConverter.SingleToInt32Bits(value));
return SetBytes(recipientSlot, entityIndex, className, fieldPath, bytes);
}
public int SetColor(int recipientSlot, int entityIndex, string className, string fieldPath, byte r, byte g, byte b, byte a)
{
Span<byte> bytes = stackalloc byte[] { r, g, b, a };
return SetBytes(recipientSlot, entityIndex, className, fieldPath, bytes);
}
public int SetVectorFirstFromField(int recipientSlot, int entityIndex, string className, string vectorFieldPath, string sourceFieldPath)
{
return _setVectorFirstFromField(recipientSlot, entityIndex, className, vectorFieldPath, sourceFieldPath);
}
public bool RemoveOverride(int ruleId)
{
return _removeOverride(ruleId);
}
public void ClearOverrides()
{
_clearOverrides();
}
public bool MarkDirty(int entityIndex, string className, string fieldPath)
{
return _markDirty(entityIndex, className, fieldPath);
}
public void Dispose()
{
NativeLibrary.Free(_library);
}
private T GetExport<T>(string name) where T : Delegate
{
return Marshal.GetDelegateForFunctionPointer<T>(NativeLibrary.GetExport(_library, name));
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private delegate int SetOverrideDelegate(int recipientSlot, int entityIndex, string className, string fieldPath, byte[] value, int valueSize);
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private delegate int SetVectorFirstFromFieldDelegate(int recipientSlot, int entityIndex, string className, string vectorFieldPath, string sourceFieldPath);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
private delegate bool RemoveOverrideDelegate(int ruleId);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void ClearOverridesDelegate();
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.I1)]
private delegate bool MarkDirtyDelegate(int entityIndex, string className, string fieldPath);
}