Compare commits

...

6 Commits

Author SHA1 Message Date
Boink
0f72631eb0 Update CommitSuicide offset for 17-11-2023 update (#98) 2023-11-18 09:43:06 +10:00
Nexd
75fcf21fb7 feat: managed coreconfig implementation (#79) 2023-11-17 17:50:59 +10:00
Nexd
0ddf6bcdfa fix: new signature for CBaseModelEntity_SetModel (#84) 2023-11-15 15:18:03 +10:00
Roflmuffin
98661cd069 fix: public and silent triggers (finally) 2023-11-14 22:29:59 +10:00
Roflmuffin
86a5699b40 feat: re-add global command listener 2023-11-14 21:15:06 +10:00
Roflmuffin
414710d05c hotfix: wrap vfunc creation in try catch to prevent all vfuncs from erroring 2023-11-13 21:40:33 +10:00
20 changed files with 324 additions and 58 deletions

View File

@@ -29,6 +29,8 @@ SET(SOURCE_FILES
src/core/utils.h
src/core/globals.h
src/core/globals.cpp
src/core/coreconfig.h
src/core/coreconfig.cpp
src/core/gameconfig.h
src/core/gameconfig.cpp
src/core/log.h
@@ -84,7 +86,7 @@ SET(SOURCE_FILES
if (LINUX)
# memoverride.cpp is not usable on CMake Windows, cuz CMake default link libraries (seems) always link ucrt.lib
set(SOURCE_FILES
set(SOURCE_FILES
${SOURCE_FILES}
libraries/hl2sdk-cs2/public/tier0/memoverride.cpp
)

View File

@@ -1,5 +1,5 @@
{
"PublicChatTrigger": "!",
"SilentChatTrigger": "/",
"PublicChatTrigger": [ "!" ],
"SilentChatTrigger": [ "/" ],
"FollowCS2ServerGuidelines": true
}

View File

@@ -50,14 +50,14 @@
"CBaseModelEntity_SetModel": {
"signatures": {
"library": "server",
"windows": "\\x48\\x89\\x5C\\x24\\x2A\\x48\\x89\\x7C\\x24\\x2A\\x55\\x48\\x8B\\xEC\\x48\\x83\\xEC\\x50\\x48\\x8B\\xF9",
"linux": "\\x55\\x48\\x89\\xF2\\x48\\x89\\xE5\\x41\\x54\\x49\\x89\\xFC\\x48\\x8D\\x7D\\xE0\\x48\\x83\\xEC\\x18\\x48\\x8D\\x05\\xE5\\xD1\\xBF\\x00"
"windows": "\\x48\\x89\\x5C\\x24\\x10\\x48\\x89\\x7C\\x24\\x20\\x55\\x48\\x8B\\xEC\\x48\\x83\\xEC\\x50\\x48\\x8B\\xF9\\x4C\\x8B\\xC2\\x48\\x8B\\x0D\\x69\\x07\\xD9\\x00",
"linux": "\\x55\\x48\\x89\\xF2\\x48\\x89\\xE5\\x41\\x54\\x49\\x89\\xFC\\x48\\x8D\\x7D\\xE0\\x48\\x83\\xEC\\x18\\x48\\x8D\\x05\\x15\\xD4\\xBF\\x00"
}
},
"CBasePlayerPawn_CommitSuicide": {
"offsets": {
"windows": 355,
"linux": 355
"windows": 356,
"linux": 356
}
},
"CBaseEntity_Teleport": {

View File

@@ -23,6 +23,7 @@ using System.Text.Json.Serialization;
using CounterStrikeSharp.API.Modules.Utils;
using CounterStrikeSharp.API.Modules.Admin;
using CounterStrikeSharp.API.Modules.Commands;
using System.Collections.Generic;
namespace CounterStrikeSharp.API.Core
{
@@ -31,9 +32,9 @@ namespace CounterStrikeSharp.API.Core
/// </summary>
internal sealed partial class CoreConfigData
{
[JsonPropertyName("PublicChatTrigger")] public string PublicChatTrigger { get; set; } = "!";
[JsonPropertyName("PublicChatTrigger")] public IEnumerable<string> PublicChatTrigger { get; set; } = new HashSet<string>() { "!" };
[JsonPropertyName("SilentChatTrigger")] public string SilentChatTrigger { get; set; } = "/";
[JsonPropertyName("SilentChatTrigger")] public IEnumerable<string> SilentChatTrigger { get; set; } = new HashSet<string>() { "/" };
[JsonPropertyName("FollowCS2ServerGuidelines")] public bool FollowCS2ServerGuidelines { get; set; } = true;
}
@@ -46,12 +47,12 @@ namespace CounterStrikeSharp.API.Core
/// <summary>
/// List of characters to use for public chat triggers.
/// </summary>
public static string PublicChatTrigger => _coreConfig.PublicChatTrigger;
public static IEnumerable<string> PublicChatTrigger => _coreConfig.PublicChatTrigger;
/// <summary>
/// List of characters to use for silent chat triggers.
/// </summary>
public static string SilentChatTrigger => _coreConfig.SilentChatTrigger;
public static IEnumerable<string> SilentChatTrigger => _coreConfig.SilentChatTrigger;
/// <summary>
/// <para>

View File

@@ -26,7 +26,7 @@ public partial class CCSPlayerController
public void PrintToConsole(string message)
{
NativeAPI.PrintToConsole((int)EntityIndex.Value.Value, message);
NativeAPI.PrintToConsole((int)EntityIndex.Value.Value, $"{message}\n\0");
}
public void PrintToChat(string message)

View File

@@ -31,9 +31,15 @@ public partial class VirtualFunction
{
if (!_createdFunctions.TryGetValue(signature, out var function))
{
function = NativeAPI.CreateVirtualFunctionBySignature(IntPtr.Zero, Addresses.ServerPath, signature,
argumentTypes.Count(), (int)returnType, arguments);
_createdFunctions[signature] = function;
try
{
function = NativeAPI.CreateVirtualFunctionBySignature(IntPtr.Zero, Addresses.ServerPath, signature,
argumentTypes.Count(), (int)returnType, arguments);
_createdFunctions[signature] = function;
}
catch (Exception)
{
}
}
return function;

View File

@@ -354,7 +354,7 @@ namespace TestPlugin
[ConsoleCommand("cssharp_attribute", "This is a custom attribute event")]
public void OnCommand(CCSPlayerController? player, CommandInfo command)
{
Log("cssharp_attribute called!");
command.ReplyToCommand("cssharp_attribute called", true);
}
[ConsoleCommand("css_changelevel", "Changes map")]

78
src/core/coreconfig.cpp Normal file
View File

@@ -0,0 +1,78 @@
/*
* This file is part of CounterStrikeSharp.
* CounterStrikeSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CounterStrikeSharp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
*/
#include <fstream>
#include "core/log.h"
#include "core/coreconfig.h"
namespace counterstrikesharp {
CCoreConfig::CCoreConfig(const std::string& path) { m_sPath = path; }
CCoreConfig::~CCoreConfig() = default;
bool CCoreConfig::Init(char* conf_error, int conf_error_size)
{
std::ifstream ifs(m_sPath);
if (!ifs) {
V_snprintf(conf_error, conf_error_size, "CoreConfig file not found.");
return false;
}
m_json = json::parse(ifs);
try {
PublicChatTrigger = m_json["PublicChatTrigger"];
SilentChatTrigger = m_json["SilentChatTrigger"];
FollowCS2ServerGuidelines = m_json["FollowCS2ServerGuidelines"];
} catch (const std::exception& ex) {
V_snprintf(conf_error, conf_error_size, "Failed to parse CoreConfig file: %s", ex.what());
return false;
}
return true;
}
const std::string CCoreConfig::GetPath() const
{
return m_sPath;
}
bool CCoreConfig::IsTriggerInternal(std::vector<std::string> triggers, const std::string& message, std::string*& prefix) const
{
for (std::string& trigger : triggers)
{
if (message.rfind(trigger, 0) == 0)
{
prefix = &trigger;
return true;
}
}
return false;
}
bool CCoreConfig::IsSilentChatTrigger(const std::string& message, std::string*& prefix) const
{
return IsTriggerInternal(SilentChatTrigger, message, prefix);
}
bool CCoreConfig::IsPublicChatTrigger(const std::string& message, std::string*& prefix) const
{
return IsTriggerInternal(PublicChatTrigger, message, prefix);
}
} // namespace counterstrikesharp

50
src/core/coreconfig.h Normal file
View File

@@ -0,0 +1,50 @@
/*
* This file is part of CounterStrikeSharp.
* CounterStrikeSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CounterStrikeSharp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CounterStrikeSharp. If not, see <https://www.gnu.org/licenses/>. *
*/
#pragma once
#include "core/globals.h"
#include <string>
#include <nlohmann/json.hpp>
namespace counterstrikesharp {
class CCoreConfig
{
public:
std::vector<std::string> PublicChatTrigger = { std::string("!") };
std::vector<std::string> SilentChatTrigger = { std::string("/") };
bool FollowCS2ServerGuidelines = true;
using json = nlohmann::json;
CCoreConfig(const std::string& path);
~CCoreConfig();
bool Init(char* conf_error, int conf_error_size);
const std::string GetPath() const;
bool IsSilentChatTrigger(const std::string& message, std::string*& prefix) const;
bool IsPublicChatTrigger(const std::string& message, std::string*& prefix) const;
private:
bool IsTriggerInternal(std::vector<std::string> triggers, const std::string& message, std::string*& prefix) const;
private:
std::string m_sPath;
json m_json;
};
} // namespace counterstrikesharp

View File

@@ -23,6 +23,8 @@
#include "const.h"
#include "utils/virtual.h"
#include <string>
#include <vector>
#include <stdint.h>
#include <type_traits>
@@ -54,6 +56,45 @@ inline uint64_t hash_64_fnv1a_const(const char *str, const uint64_t value = val_
}
namespace schema {
static std::vector<std::string> CS2BadList = {
"m_bIsValveDS",
"m_bIsQuestEligible",
"m_iItemDefinitionIndex", // in unmanaged this cannot be set.
"m_iEntityLevel",
"m_iItemIDHigh",
"m_iItemIDLow",
"m_iAccountID",
"m_iEntityQuality",
"m_bInitialized",
"m_szCustomName",
"m_iAttributeDefinitionIndex",
"m_iRawValue32",
"m_iRawInitialValue32",
"m_flValue", // MNetworkAlias "m_iRawValue32"
"m_flInitialValue", // MNetworkAlias "m_iRawInitialValue32"
"m_bSetBonus",
"m_nRefundableCurrency",
"m_OriginalOwnerXuidLow",
"m_OriginalOwnerXuidHigh",
"m_nFallbackPaintKit",
"m_nFallbackSeed",
"m_flFallbackWear",
"m_nFallbackStatTrak",
"m_iCompetitiveWins",
"m_iCompetitiveRanking",
"m_iCompetitiveRankType",
"m_iCompetitiveRankingPredicted_Win",
"m_iCompetitiveRankingPredicted_Loss",
"m_iCompetitiveRankingPredicted_Tie",
"m_nActiveCoinRank",
"m_nMusicID",
};
int16_t FindChainOffset(const char *className);
SchemaKey GetOffset(const char *className, uint32_t classKey, const char *memberName, uint32_t memberKey);
} // namespace schema

View File

@@ -65,6 +65,7 @@ SourceHook::Impl::CSourceHookImpl source_hook_impl;
SourceHook::ISourceHook *source_hook = &source_hook_impl;
ISmmAPI *ismm = nullptr;
CGameEntitySystem* entitySystem = nullptr;
CCoreConfig* coreConfig = nullptr;
CGameConfig* gameConfig = nullptr;
// Custom Managers

View File

@@ -49,6 +49,7 @@ class HookManager;
class EntityManager;
class ChatManager;
class ServerManager;
class CCoreConfig;
class CGameConfig;
namespace globals {
@@ -99,6 +100,7 @@ extern int source_hook_pluginid;
extern IGameEventSystem *gameEventSystem;
extern CounterStrikeSharpMMPlugin *mmPlugin;
extern ISmmAPI *ismm;
extern CCoreConfig* coreConfig;
extern CGameConfig* gameConfig;
void Initialize();

View File

@@ -24,6 +24,7 @@
#include <public/eiface.h>
#include "core/memory.h"
#include "core/log.h"
#include "core/coreconfig.h"
#include "core/gameconfig.h"
#include <funchook.h>
@@ -56,9 +57,6 @@ void ChatManager::OnShutdown() {}
void DetourHostSay(CBaseEntity* pController, CCommand& args, bool teamonly, int unk1,
const char* unk2)
{
CCommand newArgs;
newArgs.Tokenize(args.Arg(1));
if (pController) {
auto pEvent = globals::gameEventManager->CreateEvent("player_chat", true);
if (pEvent) {
@@ -70,48 +68,53 @@ void DetourHostSay(CBaseEntity* pController, CCommand& args, bool teamonly, int
}
}
if (*args[1] == '/' || *args[1] == '!') {
globals::chatManager.OnSayCommandPost(pController, newArgs);
return;
std::string* prefix;
bool bSilent = globals::coreConfig->IsSilentChatTrigger(args[1], prefix);
bool bCommand = bSilent || globals::coreConfig->IsPublicChatTrigger(args[1], prefix);
if (!bSilent) {
m_pHostSay(pController, args, teamonly, unk1, unk2);
}
m_pHostSay(pController, args, teamonly, unk1, unk2);
if (bCommand)
{
char *pszMessage = (char *)(args.ArgS() + prefix->length() + 1);
// Trailing slashes are only removed if Host_Say has been called.
if (bSilent)
pszMessage[V_strlen(pszMessage) - 1] = 0;
CCommand args;
args.Tokenize(pszMessage);
auto prefixedPhrase = std::string("css_") + args.Arg(0);
auto bValidWithPrefix = globals::conCommandManager.IsValidValveCommand(prefixedPhrase.c_str());
if (bValidWithPrefix) {
// Re-tokenize with a `css_` prefix if we have found that its a valid command.
args.Tokenize(("css_" + std::string(pszMessage)).c_str());
}
globals::chatManager.OnSayCommandPost(pController, args);
}
}
bool ChatManager::OnSayCommandPre(CBaseEntity* pController, CCommand& command) { return false; }
void ChatManager::OnSayCommandPost(CBaseEntity* pController, CCommand& command)
{
const char* args = command.ArgS();
auto commandStr = command.Arg(0);
return InternalDispatch(pController, commandStr + 1, command);
return InternalDispatch(pController, commandStr, command);
}
void ChatManager::InternalDispatch(CBaseEntity* pPlayerController, const char* szTriggerPhase,
CCommand& fullCommand)
{
auto ppArgV = new const char*[fullCommand.ArgC()];
ppArgV[0] = strdup(szTriggerPhase);
for (int i = 1; i < fullCommand.ArgC(); i++) {
ppArgV[i] = fullCommand.Arg(i);
}
auto prefixedPhrase = std::string("css_") + szTriggerPhase;
auto bValidWithPrefix = globals::conCommandManager.IsValidValveCommand(prefixedPhrase.c_str());
if (bValidWithPrefix) {
ppArgV[0] = prefixedPhrase.c_str();
}
CCommand commandCopy(fullCommand.ArgC(), ppArgV);
if (pPlayerController == nullptr) {
globals::conCommandManager.ExecuteCommandCallbacks(
commandCopy.Arg(0), CCommandContext(CommandTarget_t::CT_NO_TARGET, CPlayerSlot(-1)),
commandCopy, HookMode::Pre);
delete[] ppArgV;
fullCommand.Arg(0), CCommandContext(CommandTarget_t::CT_NO_TARGET, CPlayerSlot(-1)),
fullCommand, HookMode::Pre);
return;
}
@@ -119,8 +122,7 @@ void ChatManager::InternalDispatch(CBaseEntity* pPlayerController, const char* s
auto slot = CPlayerSlot(index - 1);
globals::conCommandManager.ExecuteCommandCallbacks(
commandCopy.Arg(0), CCommandContext(CommandTarget_t::CT_NO_TARGET, slot), commandCopy,
fullCommand.Arg(0), CCommandContext(CommandTarget_t::CT_NO_TARGET, slot), fullCommand,
HookMode::Pre);
delete[] ppArgV;
}
} // namespace counterstrikesharp

View File

@@ -54,7 +54,6 @@ class ChatManager : public GlobalClass
bool OnSayCommandPre(CBaseEntity* pController, CCommand& args);
void OnSayCommandPost(CBaseEntity* pController, CCommand& args);
private:
void InternalDispatch(CBaseEntity* pPlayerController, const char* szTriggerPhrase,
CCommand& pFullCommand);

View File

@@ -170,6 +170,9 @@ ConCommandInfo::~ConCommandInfo()
globals::callbackManager.ReleaseCallback(callback_pre);
globals::callbackManager.ReleaseCallback(callback_post);
}
ConCommandInfo::ConCommandInfo(bool bNoCallbacks) {
}
ConCommandManager::ConCommandManager() {}
@@ -181,6 +184,10 @@ void ConCommandManager::OnAllInitialized()
&ConCommandManager::Hook_DispatchConCommand, false);
SH_ADD_HOOK_MEMFUNC(ICvar, DispatchConCommand, globals::cvars, this,
&ConCommandManager::Hook_DispatchConCommand_Post, true);
m_global_cmd.callback_pre = globals::callbackManager.CreateCallback("OnClientCommandGlobalPre");
m_global_cmd.callback_post =
globals::callbackManager.CreateCallback("OnClientCommandGlobalPost");
}
void ConCommandManager::OnShutdown()
@@ -189,6 +196,9 @@ void ConCommandManager::OnShutdown()
&ConCommandManager::Hook_DispatchConCommand, false);
SH_REMOVE_HOOK_MEMFUNC(ICvar, DispatchConCommand, globals::cvars, this,
&ConCommandManager::Hook_DispatchConCommand_Post, true);
globals::callbackManager.ReleaseCallback(m_global_cmd.callback_pre);
globals::callbackManager.ReleaseCallback(m_global_cmd.callback_post);
}
void CommandCallback(const CCommandContext& context, const CCommand& command)
@@ -199,6 +209,15 @@ void CommandCallback(const CCommandContext& context, const CCommand& command)
void ConCommandManager::AddCommandListener(const char* name, CallbackT callback, HookMode mode)
{
if (name == nullptr) {
if (mode == HookMode::Pre) {
m_global_cmd.callback_pre->AddListener(callback);
} else {
m_global_cmd.callback_post->AddListener(callback);
}
return;
}
auto strName = std::string(name);
ConCommandInfo* pInfo = m_cmd_lookup[strName];
@@ -210,7 +229,6 @@ void ConCommandManager::AddCommandListener(const char* name, CallbackT callback,
if (hExistingCommand.IsValid()) {
pInfo->command = globals::cvars->GetCommand(hExistingCommand);
}
}
if (mode == HookMode::Pre) {
@@ -218,11 +236,19 @@ void ConCommandManager::AddCommandListener(const char* name, CallbackT callback,
} else {
pInfo->callback_post->AddListener(callback);
}
}
void ConCommandManager::RemoveCommandListener(const char* name, CallbackT callback, HookMode mode)
{
if (name == nullptr) {
if (mode == HookMode::Pre) {
m_global_cmd.callback_pre->RemoveListener(callback);
} else {
m_global_cmd.callback_post->RemoveListener(callback);
}
return;
}
auto strName = std::string(name);
ConCommandInfo* pInfo = m_cmd_lookup[strName];
@@ -285,10 +311,43 @@ bool ConCommandManager::RemoveValveCommand(const char* name)
HookResult ConCommandManager::ExecuteCommandCallbacks(const char* name, const CCommandContext& ctx,
const CCommand& args, HookMode mode)
{
CSSHARP_CORE_TRACE("[ConCommandManager::ExecuteCommandCallbacks][{}]: {}", mode == Pre ? "Pre" : "Post", name);
CSSHARP_CORE_TRACE("[ConCommandManager::ExecuteCommandCallbacks][{}]: {}",
mode == Pre ? "Pre" : "Post", name);
ConCommandInfo* pInfo = m_cmd_lookup[std::string(name)];
HookResult result = HookResult::Continue;
auto globalCallback = mode == HookMode::Pre ? m_global_cmd.callback_pre : m_global_cmd.callback_post;
if (globalCallback->GetFunctionCount() > 0) {
globalCallback->ScriptContext().Reset();
globalCallback->ScriptContext().Push(ctx.GetPlayerSlot().Get());
globalCallback->ScriptContext().Push(&args);
for (auto fnMethodToCall : globalCallback->GetFunctions()) {
if (!fnMethodToCall)
continue;
fnMethodToCall(&globalCallback->ScriptContextStruct());
auto hookResult = globalCallback->ScriptContext().GetResult<HookResult>();
if (hookResult >= HookResult::Stop) {
if (mode == HookMode::Pre) {
return HookResult::Stop;
}
result = hookResult;
break;
}
if (hookResult >= HookResult::Handled) {
result = hookResult;
}
}
}
if (!pInfo) {
return HookResult::Continue;
return result;
}
auto pCallback = mode == HookMode::Pre ? pInfo->callback_pre : pInfo->callback_post;
@@ -302,14 +361,16 @@ HookResult ConCommandManager::ExecuteCommandCallbacks(const char* name, const CC
continue;
fnMethodToCall(&pCallback->ScriptContextStruct());
auto result = pCallback->ScriptContext().GetResult<HookResult>();
auto thisResult = pCallback->ScriptContext().GetResult<HookResult>();
if (result >= HookResult::Handled) {
if (thisResult >= HookResult::Handled) {
return result;
} else if (thisResult > result) {
result = thisResult;
}
}
return HookResult::Continue;
return result;
}
void ConCommandManager::Hook_DispatchConCommand(ConCommandHandle cmd, const CCommandContext& ctx,

View File

@@ -58,6 +58,7 @@ class ConCommandInfo {
public:
ConCommandInfo();
ConCommandInfo(bool bNoCallbacks);
~ConCommandInfo();
public:
@@ -95,6 +96,7 @@ public:
private:
std::vector<ConCommandInfo*> m_cmd_list;
std::map<std::string, ConCommandInfo*, CaseInsensitiveComparator> m_cmd_lookup;
ConCommandInfo m_global_cmd = ConCommandInfo(true);
};
} // namespace counterstrikesharp

View File

@@ -20,7 +20,9 @@ inline std::string GameDirectory() {
return gameDirectory;
}
inline std::string PluginDirectory() { return GameDirectory() + "/addons/counterstrikesharp"; }
inline std::string GetRootDirectory() { return GameDirectory() + "/addons/counterstrikesharp"; }
inline std::string PluginsDirectory() { return GameDirectory() + "/addons/counterstrikesharp/plugins"; }
inline std::string ConfigsDirectory() { return GameDirectory() + "/addons/counterstrikesharp/configs"; }
inline std::string GamedataDirectory() { return GameDirectory() + "/addons/counterstrikesharp/gamedata"; }
} // namespace utils

View File

@@ -18,6 +18,7 @@
#include "core/global_listener.h"
#include "core/log.h"
#include "core/coreconfig.h"
#include "core/gameconfig.h"
#include "core/timer_system.h"
#include "core/utils.h"
@@ -83,6 +84,17 @@ bool CounterStrikeSharpMMPlugin::Load(PluginId id, ISmmAPI* ismm, char* error, s
GET_V_IFACE_ANY(GetEngineFactory, globals::gameEventSystem, IGameEventSystem,
GAMEEVENTSYSTEM_INTERFACE_VERSION);
auto coreconfig_path = std::string(utils::ConfigsDirectory() + "/core.json");
globals::coreConfig = new CCoreConfig(coreconfig_path);
char coreconfig_error[255] = "";
if (!globals::coreConfig->Init(coreconfig_error, sizeof(coreconfig_error))) {
CSSHARP_CORE_ERROR("Could not read \'{}\'. Error: {}", coreconfig_path, coreconfig_error);
return false;
}
CSSHARP_CORE_INFO("CoreConfig loaded.");
auto gamedata_path = std::string(utils::GamedataDirectory() + "/gamedata.json");
globals::gameConfig = new CGameConfig(gamedata_path);
char conf_error[255] = "";

View File

@@ -96,7 +96,7 @@ void* get_export(void* h, const char* name)
// Using the nethost library, discover the location of hostfxr and get exports
bool load_hostfxr()
{
std::string base_dir = counterstrikesharp::utils::PluginDirectory();
std::string base_dir = counterstrikesharp::utils::GetRootDirectory();
namespace css = counterstrikesharp;
#if _WIN32
std::wstring buffer =
@@ -164,7 +164,7 @@ CDotNetManager::~CDotNetManager() {}
bool CDotNetManager::Initialize()
{
const std::string base_dir = counterstrikesharp::utils::PluginDirectory();
const std::string base_dir = counterstrikesharp::utils::GetRootDirectory();
CSSHARP_CORE_INFO("Loading .NET runtime...");
@@ -184,7 +184,7 @@ bool CDotNetManager::Initialize()
std::string((base_dir + "/api/CounterStrikeSharp.API.runtimeconfig.json").c_str());
CSSHARP_CORE_INFO("Loading CSS API, Runtime Config: {}", wide_str);
#endif
const auto load_assembly_and_get_function_pointer = get_dotnet_load_assembly(wide_str.c_str());
if (load_assembly_and_get_function_pointer == nullptr) {
CSSHARP_CORE_ERROR("Failed to load CSS API.");

View File

@@ -23,6 +23,7 @@
#include "core/log.h"
#include "schema.h"
#include "core/function.h"
#include "core/coreconfig.h"
namespace counterstrikesharp {
@@ -122,6 +123,12 @@ void SetSchemaValueByName(ScriptContext& script_context)
auto dataType = script_context.GetArgument<DataType_t>(1);
auto className = script_context.GetArgument<const char*>(2);
auto memberName = script_context.GetArgument<const char*>(3);
if (globals::coreConfig->FollowCS2ServerGuidelines && std::find(schema::CS2BadList.begin(), schema::CS2BadList.end(), memberName) != schema::CS2BadList.end()) {
CSSHARP_CORE_ERROR("Cannot set '{}::{}' with \"FollowCS2ServerGuidelines\" option enabled.", className, memberName);
return;
}
auto classKey = hash_32_fnv1a_const(className);
auto memberKey = hash_32_fnv1a_const(memberName);