[Script]: Re-organize some functions (#795)

This commit is contained in:
Edo 2023-02-24 11:23:06 +01:00 committed by GitHub
parent 5975eb80ee
commit c0e4a5b4ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 172 additions and 194 deletions

View File

@ -3,13 +3,15 @@ mongoose = {
}
function mongoose.import()
links {"mongoose"}
links "mongoose"
mongoose.includes()
end
function mongoose.includes()
includedirs {mongoose.source}
includedirs {
mongoose.source,
}
end
function mongoose.project()

View File

@ -4,6 +4,7 @@
#include "Voice.hpp"
#include "GSC/Script.hpp"
#include "GSC/ScriptExtension.hpp"
namespace Components
{
@ -599,7 +600,7 @@ namespace Components
return;
}
const auto* func = Script::GetCodePosForParam(0);
const auto* func = ScriptExtension::GetCodePosForParam(0);
SayCallbacks.emplace_back(func);
});
}

View File

@ -6,9 +6,6 @@ namespace Components
std::vector<Script::ScriptFunction> Script::CustomScrFunctions;
std::vector<Script::ScriptMethod> Script::CustomScrMethods;
std::unordered_map<const char*, const char*> Script::ReplacedFunctions;
const char* Script::ReplacedPos = nullptr;
std::unordered_map<std::string, int> Script::ScriptMainHandles;
std::unordered_map<std::string, int> Script::ScriptInitHandles;
@ -204,94 +201,6 @@ namespace Components
return Game::Scr_GetNumParam();
}
const char* Script::GetCodePosForParam(int index)
{
if (static_cast<unsigned int>(index) >= Game::scrVmPub->outparamcount)
{
Game::Scr_ParamError(static_cast<unsigned int>(index), "^1GetCodePosForParam: Index is out of range!\n");
return "";
}
const auto* value = &Game::scrVmPub->top[-index];
if (value->type != Game::VAR_FUNCTION)
{
Game::Scr_ParamError(static_cast<unsigned int>(index), "^1GetCodePosForParam: Expects a function as parameter!\n");
return "";
}
return value->u.codePosValue;
}
void Script::GetReplacedPos(const char* pos)
{
if (ReplacedFunctions.contains(pos))
{
ReplacedPos = ReplacedFunctions[pos];
}
}
void Script::SetReplacedPos(const char* what, const char* with)
{
if (what[0] == '\0' || with[0] == '\0')
{
Logger::Warning(Game::CON_CHANNEL_SCRIPT, "Invalid parameters passed to ReplacedFunctions\n");
return;
}
if (ReplacedFunctions.contains(what))
{
Logger::Warning(Game::CON_CHANNEL_SCRIPT, "ReplacedFunctions already contains codePosValue for a function\n");
}
ReplacedFunctions[what] = with;
}
__declspec(naked) void Script::VMExecuteInternalStub()
{
__asm
{
pushad
push edx
call GetReplacedPos
pop edx
popad
cmp ReplacedPos, 0
jne SetPos
movzx eax, byte ptr [edx]
inc edx
Loc1:
cmp eax, 0x8B
push ecx
mov ecx, 0x2045094
mov [ecx], eax
mov ecx, 0x2040CD4
mov [ecx], edx
pop ecx
push 0x61E944
retn
SetPos:
mov edx, ReplacedPos
mov ReplacedPos, 0
movzx eax, byte ptr [edx]
inc edx
jmp Loc1
}
}
Game::client_t* Script::GetClient(const Game::gentity_t* ent)
{
assert(ent);
@ -311,71 +220,6 @@ namespace Components
return &Game::svs_clients[ent->s.number];
}
void Script::AddFunctions()
{
AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(<function>, <function>)
{
if (Game::Scr_GetNumParam() != 2)
{
Game::Scr_Error("^1ReplaceFunc: Needs two parameters!\n");
return;
}
const auto what = GetCodePosForParam(0);
const auto with = GetCodePosForParam(1);
SetReplacedPos(what, with);
});
// System time
AddFunction("GetSystemMilliseconds", [] // gsc: GetSystemMilliseconds()
{
SYSTEMTIME time;
GetSystemTime(&time);
Game::Scr_AddInt(time.wMilliseconds);
});
// Executes command to the console
AddFunction("Exec", [] // gsc: Exec(<string>)
{
const auto str = Game::Scr_GetString(0);
if (str == nullptr)
{
Game::Scr_ParamError(0, "^1Exec: Illegal parameter!\n");
return;
}
Command::Execute(str, false);
});
// Allow printing to the console even when developer is 0
AddFunction("PrintConsole", [] // gsc: PrintConsole(<string>)
{
for (std::size_t i = 0; i < Game::Scr_GetNumParam(); ++i)
{
const auto* str = Game::Scr_GetString(i);
if (str == nullptr)
{
Game::Scr_ParamError(i, "^1PrintConsole: Illegal parameter!\n");
return;
}
Logger::Print(Game::level->scriptPrintChannel, "{}", str);
}
});
// PlayerCmd_AreControlsFrozen GSC function from Black Ops 2
AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen();
{
const auto* ent = Scr_GetPlayerEntity(entref);
Game::Scr_AddBool((ent->client->flags & Game::PF_FROZEN) != 0);
});
}
Script::Script()
{
// Skip check in GScr_CheckAllowedToSetPersistentData to prevent log spam in RuntimeError.
@ -391,29 +235,5 @@ namespace Components
Utils::Hook(0x4EC8DD, BuiltIn_GetMethodStub, HOOK_CALL).install()->quick(); // Scr_GetMethod
Utils::Hook(0x5F41A3, SetExpFogStub, HOOK_CALL).install()->quick();
Utils::Hook(0x61E92E, VMExecuteInternalStub, HOOK_JUMP).install()->quick();
Utils::Hook::Nop(0x61E933, 1);
#ifdef _DEBUG
AddFunction("DebugBox", []
{
const auto* message = Game::Scr_GetString(0);
if (message == nullptr)
{
Game::Scr_Error("^1DebugBox: Illegal parameter!\n");
}
MessageBoxA(nullptr, message, "DEBUG", MB_OK);
}, true);
#endif
AddFunctions();
Events::OnVMShutdown([]
{
ReplacedFunctions.clear();
});
}
}

View File

@ -16,8 +16,6 @@ namespace Components
static Game::client_t* GetClient(const Game::gentity_t* gentity);
static const char* GetCodePosForParam(int index);
// Probably a macro 'originally' but this is fine
static Game::gentity_s* Scr_GetPlayerEntity(Game::scr_entref_t entref)
{
@ -60,9 +58,6 @@ namespace Components
static std::unordered_map<std::string, int> ScriptMainHandles;
static std::unordered_map<std::string, int> ScriptInitHandles;
static std::unordered_map<const char*, const char*> ReplacedFunctions;
static const char* ReplacedPos;
static void Scr_LoadGameType_Stub();
static void Scr_StartupGameType_Stub();
static void GScr_LoadGameTypeScript_Stub();
@ -71,11 +66,5 @@ namespace Components
static Game::BuiltinMethod BuiltIn_GetMethodStub(const char** pName, int* type);
static unsigned int SetExpFogStub();
static void GetReplacedPos(const char* pos);
static void SetReplacedPos(const char* what, const char* with);
static void VMExecuteInternalStub();
static void AddFunctions();
};
}

View File

@ -7,6 +7,9 @@ namespace Components
std::unordered_map<std::uint16_t, Game::ent_field_t> ScriptExtension::CustomEntityFields;
std::unordered_map<std::uint16_t, Game::client_fields_s> ScriptExtension::CustomClientFields;
std::unordered_map<const char*, const char*> ScriptExtension::ReplacedFunctions;
const char* ScriptExtension::ReplacedPos = nullptr;
void ScriptExtension::AddEntityField(const char* name, Game::fieldtype_t type,
const Game::ScriptCallbackEnt& setter, const Game::ScriptCallbackEnt& getter)
{
@ -106,6 +109,94 @@ namespace Components
Game::Scr_GetEntityField(entnum, offset);
}
const char* ScriptExtension::GetCodePosForParam(int index)
{
if (static_cast<unsigned int>(index) >= Game::scrVmPub->outparamcount)
{
Game::Scr_ParamError(static_cast<unsigned int>(index), "^1GetCodePosForParam: Index is out of range!\n");
return "";
}
const auto* value = &Game::scrVmPub->top[-index];
if (value->type != Game::VAR_FUNCTION)
{
Game::Scr_ParamError(static_cast<unsigned int>(index), "^1GetCodePosForParam: Expects a function as parameter!\n");
return "";
}
return value->u.codePosValue;
}
void ScriptExtension::GetReplacedPos(const char* pos)
{
if (ReplacedFunctions.contains(pos))
{
ReplacedPos = ReplacedFunctions[pos];
}
}
void ScriptExtension::SetReplacedPos(const char* what, const char* with)
{
if (!*what || !*with)
{
Logger::Warning(Game::CON_CHANNEL_SCRIPT, "Invalid parameters passed to ReplacedFunctions\n");
return;
}
if (ReplacedFunctions.contains(what))
{
Logger::Warning(Game::CON_CHANNEL_SCRIPT, "ReplacedFunctions already contains codePosValue for a function\n");
}
ReplacedFunctions[what] = with;
}
__declspec(naked) void ScriptExtension::VMExecuteInternalStub()
{
__asm
{
pushad
push edx
call GetReplacedPos
pop edx
popad
cmp ReplacedPos, 0
jne SetPos
movzx eax, byte ptr [edx]
inc edx
Loc1:
cmp eax, 0x8B
push ecx
mov ecx, 0x2045094
mov [ecx], eax
mov ecx, 0x2040CD4
mov [ecx], edx
pop ecx
push 0x61E944
ret
SetPos:
mov edx, ReplacedPos
mov ReplacedPos, 0
movzx eax, byte ptr [edx]
inc edx
jmp Loc1
}
}
void ScriptExtension::AddFunctions()
{
// Misc functions
@ -234,6 +325,57 @@ namespace Components
Game::Scr_AddInt(result);
});
Script::AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(<function>, <function>)
{
if (Game::Scr_GetNumParam() != 2)
{
Game::Scr_Error("^1ReplaceFunc: Needs two parameters!\n");
return;
}
const auto what = GetCodePosForParam(0);
const auto with = GetCodePosForParam(1);
SetReplacedPos(what, with);
});
Script::AddFunction("GetSystemMilliseconds", [] // gsc: GetSystemMilliseconds()
{
SYSTEMTIME time;
GetSystemTime(&time);
Game::Scr_AddInt(time.wMilliseconds);
});
Script::AddFunction("Exec", [] // gsc: Exec(<string>)
{
const auto* str = Game::Scr_GetString(0);
if (!str)
{
Game::Scr_ParamError(0, "^1Exec: Illegal parameter!\n");
return;
}
Command::Execute(str, false);
});
// Allow printing to the console even when developer is 0
Script::AddFunction("PrintConsole", [] // gsc: PrintConsole(<string>)
{
for (std::size_t i = 0; i < Game::Scr_GetNumParam(); ++i)
{
const auto* str = Game::Scr_GetString(i);
if (!str)
{
Game::Scr_ParamError(i, "^1PrintConsole: Illegal parameter!\n");
return;
}
Logger::Print(Game::level->scriptPrintChannel, "{}", str);
}
});
}
void ScriptExtension::AddMethods()
@ -271,6 +413,13 @@ namespace Components
client->ping = ping;
});
// PlayerCmd_AreControlsFrozen GSC function from Black Ops 2
Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen();
{
const auto* ent = Script::Scr_GetPlayerEntity(entref);
Game::Scr_AddBool((ent->client->flags & Game::PF_FROZEN) != 0);
});
}
void ScriptExtension::AddEntityFields()
@ -311,5 +460,13 @@ namespace Components
Utils::Hook(0x41BED2, Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue
Utils::Hook(0x5FBF01, Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField
Utils::Hook(0x4FF413, Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField
Utils::Hook(0x61E92E, VMExecuteInternalStub, HOOK_JUMP).install()->quick();
Utils::Hook::Nop(0x61E933, 1);
Events::OnVMShutdown([]
{
ReplacedFunctions.clear();
});
}
}

View File

@ -10,10 +10,15 @@ namespace Components
static void AddEntityField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackEnt& setter, const Game::ScriptCallbackEnt& getter);
static void AddClientField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackClient& setter, const Game::ScriptCallbackClient& getter);
static const char* GetCodePosForParam(int index);
private:
static std::unordered_map<std::uint16_t, Game::ent_field_t> CustomEntityFields;
static std::unordered_map<std::uint16_t, Game::client_fields_s> CustomClientFields;
static std::unordered_map<const char*, const char*> ReplacedFunctions;
static const char* ReplacedPos;
static void GScr_AddFieldsForEntityStub();
// Two hooks because it makes our code cleaner (luckily functions were not inlined)
@ -23,6 +28,10 @@ namespace Components
// One hook because functions were inlined
static void Scr_GetEntityFieldStub(int entnum, int offset);
static void GetReplacedPos(const char* pos);
static void SetReplacedPos(const char* what, const char* with);
static void VMExecuteInternalStub();
static void AddFunctions();
static void AddMethods();
static void AddEntityFields();