feat: add initial globals and dotnet host

This commit is contained in:
Roflmuffin
2023-10-07 16:29:43 +10:00
parent 872c59a029
commit bdeff3f12c
12 changed files with 869 additions and 40 deletions

View File

@@ -99,7 +99,7 @@ CheckOptions:
- key: readability-identifier-naming.EnumCase
value: CamelCase
- key: readability-identifier-naming.FunctionCase
value: camelBack
value: CamelCase
- key: readability-identifier-naming.MemberCase
value: CamelCase
- key: readability-identifier-naming.ParameterCase
@@ -107,5 +107,5 @@ CheckOptions:
- key: readability-identifier-naming.UnionCase
value: CamelCase
- key: readability-identifier-naming.VariableCase
value: CamelCase
value: camelBack
...

View File

@@ -9,6 +9,18 @@ SET(SOURCE_FILES
src/sample_mm.h
libraries/hl2sdk-cs2/tier1/convar.cpp
libraries/hl2sdk-cs2/public/tier0/memoverride.cpp
libraries/dotnet/hostfxr.h
libraries/dotnet/coreclr_delegates.h
"libraries/metamod-source/core/sourcehook/sourcehook.cpp"
"libraries/metamod-source/core/sourcehook/sourcehook_impl_chookidman.cpp"
"libraries/metamod-source/core/sourcehook/sourcehook_impl_chookmaninfo.cpp"
"libraries/metamod-source/core/sourcehook/sourcehook_impl_cvfnptr.cpp"
"libraries/metamod-source/core/sourcehook/sourcehook_impl_cproto.cpp"
src/scripting/dotnet_host.h
src/scripting/dotnet_host.cpp
src/core/utils.h
src/core/globals.h
src/core/globals.cpp
)

View File

@@ -0,0 +1,60 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#ifndef __CORECLR_DELEGATES_H__
#define __CORECLR_DELEGATES_H__
#include <stdint.h>
#if defined(_WIN32)
#define CORECLR_DELEGATE_CALLTYPE __stdcall
#ifdef _WCHAR_T_DEFINED
typedef wchar_t char_t;
#else
typedef unsigned short char_t;
#endif
#else
#define CORECLR_DELEGATE_CALLTYPE
typedef char char_t;
#endif
#define UNMANAGEDCALLERSONLY_METHOD ((const char_t*)-1)
// Signature of delegate returned by coreclr_delegate_type::load_assembly_and_get_function_pointer
typedef int (CORECLR_DELEGATE_CALLTYPE *load_assembly_and_get_function_pointer_fn)(
const char_t *assembly_path /* Fully qualified path to assembly */,
const char_t *type_name /* Assembly qualified type name */,
const char_t *method_name /* Public static method name compatible with delegateType */,
const char_t *delegate_type_name /* Assembly qualified delegate type name or null
or UNMANAGEDCALLERSONLY_METHOD if the method is marked with
the UnmanagedCallersOnlyAttribute. */,
void *reserved /* Extensibility parameter (currently unused and must be 0) */,
/*out*/ void **delegate /* Pointer where to store the function pointer result */);
// Signature of delegate returned by load_assembly_and_get_function_pointer_fn when delegate_type_name == null (default)
typedef int (CORECLR_DELEGATE_CALLTYPE *component_entry_point_fn)(void *arg, int32_t arg_size_in_bytes);
typedef int (CORECLR_DELEGATE_CALLTYPE *get_function_pointer_fn)(
const char_t *type_name /* Assembly qualified type name */,
const char_t *method_name /* Public static method name compatible with delegateType */,
const char_t *delegate_type_name /* Assembly qualified delegate type name or null,
or UNMANAGEDCALLERSONLY_METHOD if the method is marked with
the UnmanagedCallersOnlyAttribute. */,
void *load_context /* Extensibility parameter (currently unused and must be 0) */,
void *reserved /* Extensibility parameter (currently unused and must be 0) */,
/*out*/ void **delegate /* Pointer where to store the function pointer result */);
typedef int (CORECLR_DELEGATE_CALLTYPE *load_assembly_fn)(
const char_t *assembly_path /* Fully qualified path to assembly */,
void *load_context /* Extensibility parameter (currently unused and must be 0) */,
void *reserved /* Extensibility parameter (currently unused and must be 0) */);
typedef int (CORECLR_DELEGATE_CALLTYPE *load_assembly_bytes_fn)(
const void *assembly_bytes /* Bytes of the assembly to load */,
size_t assembly_bytes_len /* Byte length of the assembly to load */,
const void *symbols_bytes /* Optional. Bytes of the symbols for the assembly */,
size_t symbols_bytes_len /* Optional. Byte length of the symbols for the assembly */,
void *load_context /* Extensibility parameter (currently unused and must be 0) */,
void *reserved /* Extensibility parameter (currently unused and must be 0) */);
#endif // __CORECLR_DELEGATES_H__

366
libraries/dotnet/hostfxr.h Normal file
View File

@@ -0,0 +1,366 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#ifndef __HOSTFXR_H__
#define __HOSTFXR_H__
#include <stddef.h>
#include <stdint.h>
#if defined(_WIN32)
#define HOSTFXR_CALLTYPE __cdecl
#ifdef _WCHAR_T_DEFINED
typedef wchar_t char_t;
#else
typedef unsigned short char_t;
#endif
#else
#define HOSTFXR_CALLTYPE
typedef char char_t;
#endif
enum hostfxr_delegate_type
{
hdt_com_activation,
hdt_load_in_memory_assembly,
hdt_winrt_activation,
hdt_com_register,
hdt_com_unregister,
hdt_load_assembly_and_get_function_pointer,
hdt_get_function_pointer,
hdt_load_assembly,
hdt_load_assembly_bytes,
};
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_main_fn)(const int argc, const char_t **argv);
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_main_startupinfo_fn)(
const int argc,
const char_t **argv,
const char_t *host_path,
const char_t *dotnet_root,
const char_t *app_path);
typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_main_bundle_startupinfo_fn)(
const int argc,
const char_t** argv,
const char_t* host_path,
const char_t* dotnet_root,
const char_t* app_path,
int64_t bundle_header_offset);
typedef void(HOSTFXR_CALLTYPE *hostfxr_error_writer_fn)(const char_t *message);
//
// Sets a callback which is to be used to write errors to.
//
// Parameters:
// error_writer
// A callback function which will be invoked every time an error is to be reported.
// Or nullptr to unregister previously registered callback and return to the default behavior.
// Return value:
// The previously registered callback (which is now unregistered), or nullptr if no previous callback
// was registered
//
// The error writer is registered per-thread, so the registration is thread-local. On each thread
// only one callback can be registered. Subsequent registrations overwrite the previous ones.
//
// By default no callback is registered in which case the errors are written to stderr.
//
// Each call to the error writer is sort of like writing a single line (the EOL character is omitted).
// Multiple calls to the error writer may occur for one failure.
//
// If the hostfxr invokes functions in hostpolicy as part of its operation, the error writer
// will be propagated to hostpolicy for the duration of the call. This means that errors from
// both hostfxr and hostpolicy will be reporter through the same error writer.
//
typedef hostfxr_error_writer_fn(HOSTFXR_CALLTYPE *hostfxr_set_error_writer_fn)(hostfxr_error_writer_fn error_writer);
typedef void* hostfxr_handle;
struct hostfxr_initialize_parameters
{
size_t size;
const char_t *host_path;
const char_t *dotnet_root;
};
//
// Initializes the hosting components for a dotnet command line running an application
//
// Parameters:
// argc
// Number of argv arguments
// argv
// Command-line arguments for running an application (as if through the dotnet executable).
// Only command-line arguments which are accepted by runtime installation are supported, SDK/CLI commands are not supported.
// For example 'app.dll app_argument_1 app_argument_2`.
// parameters
// Optional. Additional parameters for initialization
// host_context_handle
// On success, this will be populated with an opaque value representing the initialized host context
//
// Return value:
// Success - Hosting components were successfully initialized
// HostInvalidState - Hosting components are already initialized
//
// This function parses the specified command-line arguments to determine the application to run. It will
// then find the corresponding .runtimeconfig.json and .deps.json with which to resolve frameworks and
// dependencies and prepare everything needed to load the runtime.
//
// This function only supports arguments for running an application. It does not support SDK commands.
//
// This function does not load the runtime.
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_initialize_for_dotnet_command_line_fn)(
int argc,
const char_t **argv,
const struct hostfxr_initialize_parameters *parameters,
/*out*/ hostfxr_handle *host_context_handle);
//
// Initializes the hosting components using a .runtimeconfig.json file
//
// Parameters:
// runtime_config_path
// Path to the .runtimeconfig.json file
// parameters
// Optional. Additional parameters for initialization
// host_context_handle
// On success, this will be populated with an opaque value representing the initialized host context
//
// Return value:
// Success - Hosting components were successfully initialized
// Success_HostAlreadyInitialized - Config is compatible with already initialized hosting components
// Success_DifferentRuntimeProperties - Config has runtime properties that differ from already initialized hosting components
// CoreHostIncompatibleConfig - Config is incompatible with already initialized hosting components
//
// This function will process the .runtimeconfig.json to resolve frameworks and prepare everything needed
// to load the runtime. It will only process the .deps.json from frameworks (not any app/component that
// may be next to the .runtimeconfig.json).
//
// This function does not load the runtime.
//
// If called when the runtime has already been loaded, this function will check if the specified runtime
// config is compatible with the existing runtime.
//
// Both Success_HostAlreadyInitialized and Success_DifferentRuntimeProperties codes are considered successful
// initializations. In the case of Success_DifferentRuntimeProperties, it is left to the consumer to verify that
// the difference in properties is acceptable.
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_initialize_for_runtime_config_fn)(
const char_t *runtime_config_path,
const struct hostfxr_initialize_parameters *parameters,
/*out*/ hostfxr_handle *host_context_handle);
//
// Gets the runtime property value for an initialized host context
//
// Parameters:
// host_context_handle
// Handle to the initialized host context
// name
// Runtime property name
// value
// Out parameter. Pointer to a buffer with the property value.
//
// Return value:
// The error code result.
//
// The buffer pointed to by value is owned by the host context. The lifetime of the buffer is only
// guaranteed until any of the below occur:
// - a 'run' method is called for the host context
// - properties are changed via hostfxr_set_runtime_property_value
// - the host context is closed via 'hostfxr_close'
//
// If host_context_handle is nullptr and an active host context exists, this function will get the
// property value for the active host context.
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_property_value_fn)(
const hostfxr_handle host_context_handle,
const char_t *name,
/*out*/ const char_t **value);
//
// Sets the value of a runtime property for an initialized host context
//
// Parameters:
// host_context_handle
// Handle to the initialized host context
// name
// Runtime property name
// value
// Value to set
//
// Return value:
// The error code result.
//
// Setting properties is only supported for the first host context, before the runtime has been loaded.
//
// If the property already exists in the host context, it will be overwritten. If value is nullptr, the
// property will be removed.
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_set_runtime_property_value_fn)(
const hostfxr_handle host_context_handle,
const char_t *name,
const char_t *value);
//
// Gets all the runtime properties for an initialized host context
//
// Parameters:
// host_context_handle
// Handle to the initialized host context
// count
// [in] Size of the keys and values buffers
// [out] Number of properties returned (size of keys/values buffers used). If the input value is too
// small or keys/values is nullptr, this is populated with the number of available properties
// keys
// Array of pointers to buffers with runtime property keys
// values
// Array of pointers to buffers with runtime property values
//
// Return value:
// The error code result.
//
// The buffers pointed to by keys and values are owned by the host context. The lifetime of the buffers is only
// guaranteed until any of the below occur:
// - a 'run' method is called for the host context
// - properties are changed via hostfxr_set_runtime_property_value
// - the host context is closed via 'hostfxr_close'
//
// If host_context_handle is nullptr and an active host context exists, this function will get the
// properties for the active host context.
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_properties_fn)(
const hostfxr_handle host_context_handle,
/*inout*/ size_t * count,
/*out*/ const char_t **keys,
/*out*/ const char_t **values);
//
// Load CoreCLR and run the application for an initialized host context
//
// Parameters:
// host_context_handle
// Handle to the initialized host context
//
// Return value:
// If the app was successfully run, the exit code of the application. Otherwise, the error code result.
//
// The host_context_handle must have been initialized using hostfxr_initialize_for_dotnet_command_line.
//
// This function will not return until the managed application exits.
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_run_app_fn)(const hostfxr_handle host_context_handle);
//
// Gets a typed delegate from the currently loaded CoreCLR or from a newly created one.
//
// Parameters:
// host_context_handle
// Handle to the initialized host context
// type
// Type of runtime delegate requested
// delegate
// An out parameter that will be assigned the delegate.
//
// Return value:
// The error code result.
//
// If the host_context_handle was initialized using hostfxr_initialize_for_runtime_config,
// then all delegate types are supported.
// If the host_context_handle was initialized using hostfxr_initialize_for_dotnet_command_line,
// then only the following delegate types are currently supported:
// hdt_load_assembly_and_get_function_pointer
// hdt_get_function_pointer
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_get_runtime_delegate_fn)(
const hostfxr_handle host_context_handle,
enum hostfxr_delegate_type type,
/*out*/ void **delegate);
//
// Closes an initialized host context
//
// Parameters:
// host_context_handle
// Handle to the initialized host context
//
// Return value:
// The error code result.
//
typedef int32_t(HOSTFXR_CALLTYPE *hostfxr_close_fn)(const hostfxr_handle host_context_handle);
struct hostfxr_dotnet_environment_sdk_info
{
size_t size;
const char_t* version;
const char_t* path;
};
typedef void(HOSTFXR_CALLTYPE* hostfxr_get_dotnet_environment_info_result_fn)(
const struct hostfxr_dotnet_environment_info* info,
void* result_context);
struct hostfxr_dotnet_environment_framework_info
{
size_t size;
const char_t* name;
const char_t* version;
const char_t* path;
};
struct hostfxr_dotnet_environment_info
{
size_t size;
const char_t* hostfxr_version;
const char_t* hostfxr_commit_hash;
size_t sdk_count;
const struct hostfxr_dotnet_environment_sdk_info* sdks;
size_t framework_count;
const struct hostfxr_dotnet_environment_framework_info* frameworks;
};
//
// Returns available SDKs and frameworks.
//
// Resolves the existing SDKs and frameworks from a dotnet root directory (if
// any), or the global default location. If multi-level lookup is enabled and
// the dotnet root location is different than the global location, the SDKs and
// frameworks will be enumerated from both locations.
//
// The SDKs are sorted in ascending order by version, multi-level lookup
// locations are put before private ones.
//
// The frameworks are sorted in ascending order by name followed by version,
// multi-level lookup locations are put before private ones.
//
// Parameters:
// dotnet_root
// The path to a directory containing a dotnet executable.
//
// reserved
// Reserved for future parameters.
//
// result
// Callback invoke to return the list of SDKs and frameworks.
// Structs and their elements are valid for the duration of the call.
//
// result_context
// Additional context passed to the result callback.
//
// Return value:
// 0 on success, otherwise failure.
//
// String encoding:
// Windows - UTF-16 (pal::char_t is 2 byte wchar_t)
// Unix - UTF-8 (pal::char_t is 1 byte char)
//
typedef int32_t(HOSTFXR_CALLTYPE* hostfxr_get_dotnet_environment_info_fn)(
const char_t* dotnet_root,
void* reserved,
hostfxr_get_dotnet_environment_info_result_fn result,
void* result_context);
#endif //__HOSTFXR_H__

View File

@@ -24,6 +24,7 @@ include_directories(
${SOURCESDK}/public/game/server
${METAMOD_DIR}/core
${METAMOD_DIR}/core/sourcehook
libraries
)
SET(ASMJIT_STATIC 1)

45
src/core/globals.cpp Normal file
View File

@@ -0,0 +1,45 @@
#include "core/globals.h"
#include "iserver.h"
#include "scripting/dotnet_host.h"
#include <ISmmPlugin.h>
#include <sourcehook/sourcehook.h>
#include <sourcehook/sourcehook_impl.h>
#include <public/game/server/iplayerinfo.h>
namespace counterstrikesharp
{
namespace globals
{
IVEngineServer *engine = nullptr;
IGameEventManager2 *gameEventManager = nullptr;
IPlayerInfoManager *playerinfoManager = nullptr;
IBotManager *botManager = nullptr;
IServerPluginHelpers *helpers = nullptr;
IUniformRandomStream *randomStream = nullptr;
IEngineTrace *engineTrace = nullptr;
IEngineSound *engineSound = nullptr;
INetworkStringTableContainer *netStringTables = nullptr;
CGlobalVars *globalVars = nullptr;
IFileSystem *fileSystem = nullptr;
IServerGameDLL *serverGameDll = nullptr;
IServerGameClients *serverGameClients = nullptr;
INetworkServerService *networkServerService = nullptr;
IServerTools *serverTools = nullptr;
IPhysics *physics = nullptr;
IPhysicsCollision *physicsCollision = nullptr;
IPhysicsSurfaceProps *physicsSurfaceProps = nullptr;
IMDLCache *modelCache = nullptr;
IVoiceServer *voiceServer = nullptr;
CDotNetManager dotnetManager;
ICvar *cvars = nullptr;
ISource2Server *server = nullptr;
CGlobalEntityList *globalEntityList = nullptr;
SourceHook::Impl::CSourceHookImpl source_hook_impl;
SourceHook::ISourceHook *source_hook = &source_hook_impl;
int source_hook_pluginid = 0;
} // namespace globals
} // namespace counterstrikesharp

89
src/core/globals.h Normal file
View File

@@ -0,0 +1,89 @@
#pragma once
#include "ISmmAPI.h"
#include "eiface.h"
#include "iserver.h"
#include <sourcehook/sourcehook.h>
class IGameEventManager2;
class IPlayerInfoManager;
class IBotManager;
class IServerPluginHelpers;
class IUniformRandomStream;
class IEngineTrace;
class IEngineSound;
class INetworkStringTableContainer;
class CGlobalVars;
class IFileSystem;
class IServerTools;
class IPhysics;
class IPhysicsCollision;
class IPhysicsSurfaceProps;
class IMDLCache;
class IVoiceServer;
class CGlobalEntityList;
class CDotNetManager;
class ICvar;
namespace counterstrikesharp
{
class EntityListener;
class EventManager;
class UserMessageManager;
class ConCommandManager;
class CallbackManager;
class ConVarManager;
class PlayerManager;
class MenuManager;
class TimerSystem;
class ChatCommands;
class HookManager;
namespace globals
{
extern IVEngineServer *engine;
extern IGameEventManager2 *gameeventmanager;
extern IPlayerInfoManager *playerinfoManager;
extern IBotManager *botManager;
extern IServerPluginHelpers *helpers;
extern IUniformRandomStream *randomStream;
extern IEngineTrace *engineTrace;
extern IEngineSound *engineSound;
extern INetworkStringTableContainer *netStringTables;
extern CGlobalVars *globalVars;
extern IFileSystem *fileSystem;
extern IServerGameDLL *serverGameDll;
extern IServerGameClients *serverGameClients;
extern INetworkServerService *networkServerService;
extern IServerTools *serverTools;
extern IPhysics *physics;
extern IPhysicsCollision *physicsCollision;
extern IPhysicsSurfaceProps *physicsSurfaceProps;
extern IMDLCache *modelCache;
extern IVoiceServer *voiceServer;
extern CDotNetManager dotnetManager;
extern ICvar *cvars;
extern ISource2Server *server;
extern CGlobalEntityList *globalEntityList;
extern EntityListener entityListener;
extern EventManager eventManager;
extern UserMessageManager userMessageManager;
extern ConCommandManager conCommandManager;
extern CallbackManager callbackManager;
extern ConVarManager convarManager;
extern PlayerManager playerManager;
extern MenuManager menuManager;
extern TimerSystem timerSystem;
extern ChatCommands chatCommands;
extern HookManager hookManager;
extern SourceHook::ISourceHook *source_hook;
extern int source_hook_pluginid;
} // namespace globals
} // namespace counterstrikesharp
#undef SH_GLOB_SHPTR
#define SH_GLOB_SHPTR counterstrikesharp::globals::source_hook
#undef SH_GLOB_PLUGPTR
#define SH_GLOB_PLUGPTR counterstrikesharp::globals::source_hook_pluginid

37
src/core/utils.h Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
#include <public/eiface.h>
#include <string>
#include "core/globals.h"
namespace counterstrikesharp
{
namespace utils
{
static std::string gameDirectory;
inline std::string GameDirectory()
{
if (gameDirectory.empty())
{
CBufferStringGrowable<255> gamePath;
globals::engine->GetGameDir(gamePath);
gameDirectory = std::string(gamePath.Get());
}
return gameDirectory;
}
inline std::string PluginDirectory()
{
return GameDirectory() + "/addons/counterstrikesharp";
}
inline std::string ConfigDirectory()
{
return PluginDirectory() + "/config";
}
} // namespace utils
} // namespace counterstrikesharp

View File

@@ -17,8 +17,11 @@
#include <cstdio>
#include "core/utils.h"
#include "iserver.h"
namespace counterstrikesharp
{
SH_DECL_HOOK3_void(IServerGameDLL, GameFrame, SH_NOATTRIB, 0, bool, bool, bool);
SH_DECL_HOOK4_void(IServerGameClients, ClientActive, SH_NOATTRIB, 0, CPlayerSlot, bool, const char *, uint64);
SH_DECL_HOOK5_void(IServerGameClients, ClientDisconnect, SH_NOATTRIB, 0, CPlayerSlot, int, const char *, uint64,
@@ -34,11 +37,7 @@ SH_DECL_HOOK2(IGameEventManager2, FireEvent, SH_NOATTRIB, 0, bool, IGameEvent *,
SH_DECL_HOOK2_void(IServerGameClients, ClientCommand, SH_NOATTRIB, 0, CPlayerSlot, const CCommand &);
SamplePlugin g_SamplePlugin;
IServerGameDLL *server = nullptr;
IServerGameClients *gameclients = nullptr;
IVEngineServer *engine = nullptr;
IGameEventManager2 *gameevents = nullptr;
ICvar *icvar = nullptr;
// Should only be called within the active game loop (i e map should be loaded
// and active) otherwise that'll be nullptr!
@@ -59,7 +58,8 @@ ConVar sample_cvar("sample_cvar", "42", 0);
CON_COMMAND_F(sample_command, "Sample command", FCVAR_NONE)
{
META_CONPRINTF("Sample command called by %d. Command: %s\n", context.GetPlayerSlot(), args.GetCommandString());
META_CONPRINTF("Sample command called by %d. Command: %s\n", context.GetPlayerSlot(),
utils::PluginDirectory().c_str());
}
PLUGIN_EXPOSE(SamplePlugin, g_SamplePlugin);
@@ -67,34 +67,38 @@ bool SamplePlugin::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen,
{
PLUGIN_SAVEVARS();
GET_V_IFACE_CURRENT(GetEngineFactory, engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER);
GET_V_IFACE_CURRENT(GetEngineFactory, icvar, ICvar, CVAR_INTERFACE_VERSION);
GET_V_IFACE_ANY(GetServerFactory, server, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL);
GET_V_IFACE_ANY(GetServerFactory, gameclients, IServerGameClients, INTERFACEVERSION_SERVERGAMECLIENTS);
GET_V_IFACE_ANY(GetEngineFactory, g_pNetworkServerService, INetworkServerService,
NETWORKSERVERSERVICE_INTERFACE_VERSION);
META_CONPRINTF("ISMM: %d", ismm);
// Currently doesn't work from within mm side, use GetGameGlobals() in the
// mean time instead gpGlobals = ismm->GetCGlobals();
GET_V_IFACE_CURRENT(GetEngineFactory, globals::engine, IVEngineServer, INTERFACEVERSION_VENGINESERVER);
GET_V_IFACE_CURRENT(GetEngineFactory, globals::cvars, ICvar, CVAR_INTERFACE_VERSION);
GET_V_IFACE_ANY(GetServerFactory, globals::server, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL);
GET_V_IFACE_ANY(GetServerFactory, globals::serverGameClients, IServerGameClients,
INTERFACEVERSION_SERVERGAMECLIENTS);
GET_V_IFACE_ANY(GetEngineFactory, globals::networkServerService, INetworkServerService,
NETWORKSERVERSERVICE_INTERFACE_VERSION);
META_CONPRINTF("Starting plugin.\n");
SH_ADD_HOOK_MEMFUNC(IServerGameDLL, GameFrame, server, this, &SamplePlugin::Hook_GameFrame, true);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientActive, gameclients, this, &SamplePlugin::Hook_ClientActive, true);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, gameclients, this, &SamplePlugin::Hook_ClientDisconnect,
true);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientPutInServer, gameclients, this, &SamplePlugin::Hook_ClientPutInServer,
true);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientSettingsChanged, gameclients, this,
SH_ADD_HOOK_MEMFUNC(IServerGameDLL, GameFrame, globals::server, this, &SamplePlugin::Hook_GameFrame, true);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientActive, globals::serverGameClients, this,
&SamplePlugin::Hook_ClientActive, true);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, globals::serverGameClients, this,
&SamplePlugin::Hook_ClientDisconnect, true);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientPutInServer, globals::serverGameClients, this,
&SamplePlugin::Hook_ClientPutInServer, true);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientSettingsChanged, globals::serverGameClients, this,
&SamplePlugin::Hook_ClientSettingsChanged, false);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, OnClientConnected, gameclients, this, &SamplePlugin::Hook_OnClientConnected,
false);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientConnect, gameclients, this, &SamplePlugin::Hook_ClientConnect, false);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientCommand, gameclients, this, &SamplePlugin::Hook_ClientCommand, false);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, OnClientConnected, globals::serverGameClients, this,
&SamplePlugin::Hook_OnClientConnected, false);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientConnect, globals::serverGameClients, this,
&SamplePlugin::Hook_ClientConnect, false);
SH_ADD_HOOK_MEMFUNC(IServerGameClients, ClientCommand, globals::serverGameClients, this,
&SamplePlugin::Hook_ClientCommand, false);
META_CONPRINTF("All hooks started!\n");
g_pCVar = icvar;
// Used by Metamod Console Commands
g_pCVar = globals::cvars;
ConVar_Register(FCVAR_RELEASE | FCVAR_CLIENT_CAN_EXECUTE | FCVAR_GAMEDLL);
return true;
@@ -102,21 +106,21 @@ bool SamplePlugin::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen,
bool SamplePlugin::Unload(char *error, size_t maxlen)
{
SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, GameFrame, server, this, &SamplePlugin::Hook_GameFrame, true);
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientActive, gameclients, this, &SamplePlugin::Hook_ClientActive, true);
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, gameclients, this,
SH_REMOVE_HOOK_MEMFUNC(IServerGameDLL, GameFrame, globals::server, this, &SamplePlugin::Hook_GameFrame, true);
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientActive, globals::serverGameClients, this,
&SamplePlugin::Hook_ClientActive, true);
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientDisconnect, globals::serverGameClients, this,
&SamplePlugin::Hook_ClientDisconnect, true);
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientPutInServer, gameclients, this,
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientPutInServer, globals::serverGameClients, this,
&SamplePlugin::Hook_ClientPutInServer, true);
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientSettingsChanged, gameclients, this,
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientSettingsChanged, globals::serverGameClients, this,
&SamplePlugin::Hook_ClientSettingsChanged, false);
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, OnClientConnected, gameclients, this,
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, OnClientConnected, globals::serverGameClients, this,
&SamplePlugin::Hook_OnClientConnected, false);
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientConnect, gameclients, this, &SamplePlugin::Hook_ClientConnect,
false);
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientCommand, gameclients, this, &SamplePlugin::Hook_ClientCommand,
false);
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientConnect, globals::serverGameClients, this,
&SamplePlugin::Hook_ClientConnect, false);
SH_REMOVE_HOOK_MEMFUNC(IServerGameClients, ClientCommand, globals::serverGameClients, this,
&SamplePlugin::Hook_ClientCommand, false);
return true;
}
@@ -240,4 +244,5 @@ const char *SamplePlugin::GetName()
const char *SamplePlugin::GetURL()
{
return "http://www.sourcemm.net/";
}
}
} // namespace counterstrikesharp

View File

@@ -22,6 +22,8 @@
#include <iplayerinfo.h>
#include <sh_vector.h>
namespace counterstrikesharp
{
class SamplePlugin : public ISmmPlugin, public IMetamodListener
{
public:
@@ -64,4 +66,5 @@ PLUGIN_GLOBALVARS();
#endif //_INCLUDE_METAMOD_SOURCE_STUB_PLUGIN_H_
#pragma clang diagnostic pop
#pragma clang diagnostic pop
}

View File

@@ -0,0 +1,175 @@
#include "scripting/dotnet_host.h"
#include <dotnet/coreclr_delegates.h>
#include <dotnet/hostfxr.h>
#include <codecvt>
#include <locale>
#ifdef WIN32
#include <Windows.h>
#include <direct.h>
#define STR(s) L##s
#define CH(c) L##c
#define DIR_SEPARATOR L'\\'
#else
#include <dlfcn.h>
#endif
#include <cassert>
#include <iostream>
#include "core/utils.h"
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
namespace
{
hostfxr_initialize_for_runtime_config_fn init_fptr;
hostfxr_get_runtime_delegate_fn get_delegate_fptr;
hostfxr_close_fn close_fptr;
hostfxr_handle cxt;
bool load_hostfxr();
load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t *assembly);
} // namespace
namespace
{
// Forward declarations
void *load_library(const char_t *);
void *get_export(void *, const char *);
#ifdef _WINDOWS
void *load_library(const char_t *path)
{
HMODULE h = ::LoadLibraryW(path);
assert(h != nullptr);
return (void *)h;
}
void *get_export(void *h, const char *name)
{
void *f = ::GetProcAddress((HMODULE)h, name);
assert(f != nullptr);
return f;
}
#else
void *load_library(const char_t *path)
{
void *h = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
assert(h != nullptr);
return h;
}
void *get_export(void *h, const char *name)
{
void *f = dlsym(h, name);
assert(f != nullptr);
return f;
}
#endif
// <SnippetLoadHostFxr>
// Using the nethost library, discover the location of hostfxr and get exports
bool load_hostfxr()
{
std::string baseDir = counterstrikesharp::utils::PluginDirectory();
std::string buffer = std::string(baseDir + "/dotnet/host/fxr/8.0.0-rc.1.23419.4/hostfxr.dll");
// Load hostfxr and get desired exports
void *lib = load_library(buffer.c_str());
init_fptr = (hostfxr_initialize_for_runtime_config_fn)get_export(lib, "hostfxr_initialize_for_runtime_config");
get_delegate_fptr = (hostfxr_get_runtime_delegate_fn)get_export(lib, "hostfxr_get_runtime_delegate");
close_fptr = (hostfxr_close_fn)get_export(lib, "hostfxr_close");
return (init_fptr && get_delegate_fptr && close_fptr);
}
// </SnippetLoadHostFxr>
// <SnippetInitialize>
// Load and initialize .NET Core and get desired function pointer for scenario
load_assembly_and_get_function_pointer_fn get_dotnet_load_assembly(const char_t *config_path)
{
// Load .NET Core
void *load_assembly_and_get_function_pointer = nullptr;
int rc = init_fptr(config_path, nullptr, &cxt);
if (rc != 0 || cxt == nullptr)
{
std::cerr << "Init failed: " << std::hex << std::showbase << rc << std::endl;
close_fptr(cxt);
return nullptr;
}
// Get the load assembly function pointer
rc = get_delegate_fptr(cxt, hdt_load_assembly_and_get_function_pointer, &load_assembly_and_get_function_pointer);
if (rc != 0 || load_assembly_and_get_function_pointer == nullptr)
std::cerr << "Get delegate failed: " << std::hex << std::showbase << rc << std::endl;
// close_fptr(cxt);
return (load_assembly_and_get_function_pointer_fn)load_assembly_and_get_function_pointer;
}
} // namespace
CDotNetManager::CDotNetManager()
{
}
CDotNetManager::~CDotNetManager()
{
}
bool CDotNetManager::Initialize()
{
std::string baseDir = counterstrikesharp::utils::PluginDirectory();
if (!load_hostfxr())
{
// // VSPDN_CORE_ERROR("Failed to initialize .NET");
return false;
}
std::string wideStr = std::string((baseDir + "/api/CounterStrikeSharp.API.runtimeconfig.json").c_str());
load_assembly_and_get_function_pointer_fn load_assembly_and_get_function_pointer = nullptr;
load_assembly_and_get_function_pointer = get_dotnet_load_assembly(wideStr.c_str());
assert(load_assembly_and_get_function_pointer != nullptr && "Failure: get_dotnet_load_assembly()");
const std::string dotnetlib_path = std::string((baseDir + "/api/CounterStrikeSharp.API.dll").c_str());
const char_t *dotnet_type = "CounterStrikeSharp.API.Core.Helpers, CounterStrikeSharp.API";
// Namespace, assembly name
typedef int(CORECLR_DELEGATE_CALLTYPE * custom_entry_point_fn)();
custom_entry_point_fn entry_point = nullptr;
int rc =
load_assembly_and_get_function_pointer(dotnetlib_path.c_str(), dotnet_type, "LoadAllPlugins" /*method_name*/,
UNMANAGEDCALLERSONLY_METHOD, nullptr, (void **)&entry_point);
assert(rc == 0 && entry_point != nullptr && "Failure: load_assembly_and_get_function_pointer()");
const bool success = entry_point();
if (!success)
{
// VSPDN_CORE_ERROR("Failed to initialize .NET");
return false;
}
// VSPDN_CORE_INFO(".NET Initialized.");
return true;
}
void CDotNetManager::UnloadPlugin(PluginContext *context)
{
}
void CDotNetManager::Shutdown()
{
// CoreCLR does not currently supporting unloading... :(
}
PluginContext *CDotNetManager::FindContext(std::string path)
{
return nullptr;
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
class PluginContext
{
friend class CDotNetManager;
public:
PluginContext(std::string dll_path) : m_dll_path(dll_path)
{
}
private:
std::string m_dll_path;
};
class CDotNetManager
{
friend class PluginContext;
public:
CDotNetManager();
~CDotNetManager();
bool Initialize();
void UnloadPlugin(PluginContext *context);
void Shutdown();
PluginContext *FindContext(std::string path);
private:
std::vector<std::shared_ptr<PluginContext>> m_app_domains;
};