diff --git a/Template/Metahook.Plugin/Exportfuncs.cs b/Template/Metahook.Plugin/Exportfuncs.cs new file mode 100644 index 0000000..d6764b3 --- /dev/null +++ b/Template/Metahook.Plugin/Exportfuncs.cs @@ -0,0 +1,51 @@ +using GoldSrc.HLSDK.Native; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static Plugin.Global; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace Plugin; +public unsafe static class Exportfuncs +{ + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + public static int Initialize(cl_enginefunc_t *pEnginefuncs, int iVersion) + { + var s = sizeof(cl_enginefunc_t); + + gEngfuncs = *pEnginefuncs; + return gExportfuncs.Initialize(pEnginefuncs, iVersion); + } + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + public static void HUD_Init() + { + gExportfuncs.HUD_Init(); + } + static int count = 0; + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])] + public static int HUD_Redraw(float time, int intermission) + { + if (count < 10) + { + using var str = $"Hello World!!!!!(hit:{count + 1})\n".GetNativeString(); + gEngfuncs.Con_Printf(str); + count++; + } + return gExportfuncs.HUD_Redraw(time, intermission); + } +} + + +public static class StringHelper +{ + public unsafe static NativeString GetNativeString(this string s) => new NativeString() { c_str = (sbyte*)Marshal.StringToHGlobalAnsi(s) }; + +} + +public unsafe struct NativeString : IDisposable +{ + internal sbyte* c_str; + public void Dispose() => Marshal.FreeHGlobal((nint)c_str); + public static implicit operator sbyte*(NativeString d) => d.c_str; + public static implicit operator byte*(NativeString d) => (byte*)d.c_str; + public static implicit operator nint(NativeString d) => (nint)d.c_str; +} \ No newline at end of file diff --git a/Template/Metahook.Plugin/Global.cs b/Template/Metahook.Plugin/Global.cs new file mode 100644 index 0000000..8918f36 --- /dev/null +++ b/Template/Metahook.Plugin/Global.cs @@ -0,0 +1,13 @@ +using GoldSrc.HLSDK.Native; +using GoldSrc.Metahook.Native; + +namespace Plugin; + +public unsafe static class Global +{ + public static cl_exportfuncs_t gExportfuncs; + public static mh_interface_t* g_pInterface; + public static metahook_api_t* g_pMetaHookAPI; + public static mh_enginesave_t* g_pMetaSave; + public static cl_enginefunc_t gEngfuncs; +} diff --git a/Template/Metahook.Plugin/Metahook.Plugin.Template.nuspec b/Template/Metahook.Plugin/Metahook.Plugin.Template.nuspec new file mode 100644 index 0000000..bfb4753 --- /dev/null +++ b/Template/Metahook.Plugin/Metahook.Plugin.Template.nuspec @@ -0,0 +1,15 @@ + + + + Metahook.Plugin.Template + 1.0.0-alpha + Metahook Plugin + CeSun + + + + + + + + \ No newline at end of file diff --git a/Template/Metahook.Plugin/PluginReplaceName.csproj b/Template/Metahook.Plugin/PluginReplaceName.csproj new file mode 100644 index 0000000..e404687 --- /dev/null +++ b/Template/Metahook.Plugin/PluginReplaceName.csproj @@ -0,0 +1,17 @@ + + + + net9.0 + enable + enable + true + true + Plugin + + + + + + + + diff --git a/Template/Metahook.Plugin/PluginV1.cs b/Template/Metahook.Plugin/PluginV1.cs new file mode 100644 index 0000000..92f4483 --- /dev/null +++ b/Template/Metahook.Plugin/PluginV1.cs @@ -0,0 +1,26 @@ +using GoldSrc.HLSDK.Native; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Plugin; + +public class PluginV1 +{ + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvThiscall)])] + public static unsafe void Init(nint self, cl_exportfuncs_t* pSave) + { + + } + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvThiscall)])] + public static unsafe void Shutdown(nint self, int restart) + { + + } + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvThiscall)])] + public static unsafe void Destructor(nint self) + { + + } +} diff --git a/Template/Metahook.Plugin/PluginV2.cs b/Template/Metahook.Plugin/PluginV2.cs new file mode 100644 index 0000000..b4c3d21 --- /dev/null +++ b/Template/Metahook.Plugin/PluginV2.cs @@ -0,0 +1,55 @@ +using GoldSrc.HLSDK.Native; +using GoldSrc.Metahook.Native; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using static Plugin.Global; + +namespace Plugin; + +public unsafe static class PluginV2 +{ + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvThiscall)])] + public static unsafe void Init(nint self, metahook_api_t* pAPI, mh_interface_t* pInterface, mh_enginesave_t* pSave) + { + g_pInterface = pInterface; + g_pMetaHookAPI = pAPI; + g_pMetaSave = pSave; + } + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvThiscall)])] + public static unsafe void LoadClient(nint self, cl_exportfuncs_t* pExportFunc) + { + gExportfuncs = *pExportFunc; + + pExportFunc->Initialize = &Exportfuncs.Initialize; + pExportFunc->HUD_Init = &Exportfuncs.HUD_Init; + pExportFunc->HUD_Redraw = &Exportfuncs.HUD_Redraw; + } + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvThiscall)])] + public static unsafe void LoadEngine(nint self) + { + + } + + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvThiscall)])] + public static unsafe void ExitGame(nint self, int iResult) + { + + } + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvThiscall)])] + public static unsafe void Shutdown(nint self) + { + + } + + + [UnmanagedCallersOnly(CallConvs = [typeof(CallConvThiscall)])] + public static unsafe void Destructor(nint self) + { + + } +} diff --git a/Template/Metahook.Plugin/Register.cs b/Template/Metahook.Plugin/Register.cs new file mode 100644 index 0000000..5776ccf --- /dev/null +++ b/Template/Metahook.Plugin/Register.cs @@ -0,0 +1,75 @@ +using GoldSrc.Metahook.Native; +using GoldSrc.Metahook; +using GoldSrc.HLSDK.Native; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Plugin; + +public static class Register +{ + + [UnmanagedCallersOnly(EntryPoint = "CreateInterface", CallConvs = [typeof(CallConvCdecl)])] + public static unsafe nint CreateInterface(nint ptr, int* num) + { + string? frameworkVersion = Marshal.PtrToStringAnsi(ptr); + if (frameworkVersion == null) + return IntPtr.Zero; + nint pPlugin = IntPtr.Zero; + int result = 1; + switch (frameworkVersion) + { + case PluginConst.METAHOOK_PLUGIN_API_VERSION_V2: + (pPlugin, result) = GetV2PluginInstance(frameworkVersion); + break; + case PluginConst.METAHOOK_PLUGIN_API_VERSION_V1: + (pPlugin, result) = GetV1PluginInstance(frameworkVersion); + break; + } + return pPlugin; + } + + + public static unsafe (nint, int) GetV1PluginInstance(string version) + { + var ptr = Marshal.AllocHGlobal(sizeof(PluginValueType)); + ref var plugin = ref Unsafe.AsRef((void*)ptr); + var functionTable = Marshal.AllocHGlobal(sizeof(nint) * 3); + Span functions = new Span((void*)functionTable, 3); + functions[0] = (nint)(delegate* unmanaged[Thiscall])&PluginV1.Destructor; + functions[1] = (nint)(delegate* unmanaged[Thiscall])&PluginV1.Init; + functions[2] = (nint)(delegate* unmanaged[Thiscall])&PluginV1.Shutdown; + plugin = new PluginValueType + { + VirtualFunctionTable = functionTable + }; + return (ptr, 0); + } + + + public static unsafe (nint, int) GetV2PluginInstance(string version) + { + var ptr = Marshal.AllocHGlobal(sizeof(PluginValueType)); + ref var plugin = ref Unsafe.AsRef((void*)ptr); + var functionTable = Marshal.AllocHGlobal(sizeof(nint) * 6); + Span functions = new Span((void*)functionTable, 6); + functions[0] = (nint)(delegate* unmanaged[Thiscall])&PluginV2.Destructor; + functions[1] = (nint)(delegate* unmanaged[Thiscall])&PluginV2.Init; + functions[2] = (nint)(delegate* unmanaged[Thiscall])&PluginV2.Shutdown; + functions[3] = (nint)(delegate* unmanaged[Thiscall])&PluginV2.LoadEngine; + functions[4] = (nint)(delegate* unmanaged[Thiscall])&PluginV2.LoadClient; + functions[5] = (nint)(delegate* unmanaged[Thiscall])&PluginV2.ExitGame; + plugin = new PluginValueType + { + VirtualFunctionTable = functionTable + }; + return (ptr, 0); + } + + struct PluginValueType + { + public nint VirtualFunctionTable; + } +} + +