[Script] Re-work stuff (no hidden changes)
*maybe* (Based on a true story and events)
This commit is contained in:
parent
22d57a675e
commit
60f7c7c254
@ -33,7 +33,6 @@
|
||||
| `-stdout` | Redirect all logging output to the terminal iw4x is started from, or if there is none, creates a new terminal window to write log information in. |
|
||||
| `-console` | Allow the game to display its own separate interactive console window. |
|
||||
| `-dedicated` | Starts the game as a headless dedicated server. |
|
||||
| `-scriptablehttp` | Enable HTTP related gsc functions. |
|
||||
| `-bigminidumps` | Include all code sections from loaded modules in the dump. |
|
||||
| `-reallybigminidumps` | Include data sections from all loaded modules in the dump. |
|
||||
| `-dump` | Write info of loaded assets to the raw folder as they are being loaded. |
|
||||
|
@ -49,7 +49,6 @@ namespace Components
|
||||
Loader::Register(new Zones());
|
||||
Loader::Register(new D3D9Ex());
|
||||
Loader::Register(new Logger());
|
||||
Loader::Register(new Script());
|
||||
Loader::Register(new Weapon());
|
||||
Loader::Register(new Window());
|
||||
Loader::Register(new Command());
|
||||
@ -105,7 +104,6 @@ namespace Components
|
||||
Loader::Register(new Elevators());
|
||||
Loader::Register(new ClientCommand());
|
||||
Loader::Register(new VisionFile());
|
||||
Loader::Register(new ScriptExtension());
|
||||
Loader::Register(new Branding());
|
||||
Loader::Register(new Debug());
|
||||
Loader::Register(new RawMouse());
|
||||
@ -115,6 +113,8 @@ namespace Components
|
||||
Loader::Register(new UserInfo());
|
||||
Loader::Register(new Events());
|
||||
|
||||
Loader::Register(new GSC());
|
||||
|
||||
Loader::Pregame = false;
|
||||
|
||||
// Make sure preDestroy is called when the game shuts down
|
||||
|
@ -74,7 +74,6 @@ namespace Components
|
||||
#include "Modules/Toast.hpp"
|
||||
#include "Modules/Zones.hpp"
|
||||
#include "Modules/D3D9Ex.hpp"
|
||||
#include "Modules/Script.hpp"
|
||||
#include "Modules/Weapon.hpp"
|
||||
#include "Modules/Window.hpp"
|
||||
#include "Modules/Command.hpp"
|
||||
@ -136,7 +135,6 @@ namespace Components
|
||||
#include "Modules/ClientCommand.hpp"
|
||||
#include "Modules/VisionFile.hpp"
|
||||
#include "Modules/Gamepad.hpp"
|
||||
#include "Modules/ScriptExtension.hpp"
|
||||
#include "Modules/Branding.hpp"
|
||||
#include "Modules/Debug.hpp"
|
||||
#include "Modules/RawMouse.hpp"
|
||||
@ -145,3 +143,5 @@ namespace Components
|
||||
#include "Modules/Ceg.hpp"
|
||||
#include "Modules/UserInfo.hpp"
|
||||
#include "Modules/Events.hpp"
|
||||
|
||||
#include "Modules/GSC/GSC.hpp"
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "GSC/Script.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
@ -121,24 +122,6 @@ namespace Components
|
||||
Script::AddMethod("IsBot", Bots::GScr_isTestClient); // Usage: self IsBot();
|
||||
Script::AddMethod("IsTestClient", Bots::GScr_isTestClient); // Usage: self IsTestClient();
|
||||
|
||||
Script::AddMethod("SetPing", [](Game::scr_entref_t entref) // gsc: self SetPing(<int>)
|
||||
{
|
||||
auto ping = Game::Scr_GetInt(0);
|
||||
|
||||
ping = std::clamp(ping, 0, 999);
|
||||
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
auto* client = Script::GetClient(ent);
|
||||
|
||||
if (Game::SV_IsTestClient(ent->s.number) == 0)
|
||||
{
|
||||
Game::Scr_Error("^1SetPing: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
client->ping = static_cast<int16_t>(ping);
|
||||
});
|
||||
|
||||
Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: <bot> BotStop();
|
||||
{
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
@ -201,7 +184,7 @@ namespace Components
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0u; i < std::extent_v<decltype(BotActions)>; ++i)
|
||||
for (std::size_t i = 0; i < std::extent_v<decltype(BotActions)>; ++i)
|
||||
{
|
||||
if (Utils::String::ToLower(&action[1]) != BotActions[i].action)
|
||||
continue;
|
||||
@ -331,9 +314,9 @@ namespace Components
|
||||
});
|
||||
|
||||
// Zero the bot command array
|
||||
for (auto i = 0u; i < std::extent_v<decltype(g_botai)>; i++)
|
||||
for (std::size_t i = 0; i < std::extent_v<decltype(g_botai)>; ++i)
|
||||
{
|
||||
g_botai[i] = {0};
|
||||
std::memset(&g_botai[i], 0, sizeof(BotMovementInfo));
|
||||
g_botai[i].weapon = 1; // Prevent the bots from defaulting to the 'none' weapon
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "GSC/Script.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "GSC/Script.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "GSC/Script.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
@ -968,9 +969,6 @@ namespace Components
|
||||
|
||||
Script::AddFunction("HttpGet", []
|
||||
{
|
||||
if (!Flags::HasFlag("scriptablehttp"))
|
||||
return;
|
||||
|
||||
const auto* url = Game::Scr_GetString(0);
|
||||
|
||||
if (url == nullptr)
|
||||
@ -989,9 +987,6 @@ namespace Components
|
||||
|
||||
Script::AddFunction("HttpCancel", []
|
||||
{
|
||||
if (!Flags::HasFlag("scriptablehttp"))
|
||||
return;
|
||||
|
||||
const auto object = Game::Scr_GetObject(0);
|
||||
for (const auto& download : Download::ScriptDownloads)
|
||||
{
|
||||
|
17
src/Components/Modules/GSC/GSC.cpp
Normal file
17
src/Components/Modules/GSC/GSC.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
#include "IO.hpp"
|
||||
#include "Script.hpp"
|
||||
#include "ScriptExtension.hpp"
|
||||
#include "ScriptStorage.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
GSC::GSC()
|
||||
{
|
||||
Loader::Register(new IO());
|
||||
Loader::Register(new Script());
|
||||
Loader::Register(new ScriptExtension());
|
||||
Loader::Register(new ScriptStorage());
|
||||
}
|
||||
}
|
10
src/Components/Modules/GSC/GSC.hpp
Normal file
10
src/Components/Modules/GSC/GSC.hpp
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class GSC : public Component
|
||||
{
|
||||
public:
|
||||
GSC();
|
||||
};
|
||||
}
|
115
src/Components/Modules/GSC/IO.cpp
Normal file
115
src/Components/Modules/GSC/IO.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IO.hpp"
|
||||
#include "Script.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
const char* IO::QueryStrings[] = { R"(..)", R"(../)", R"(..\)" };
|
||||
|
||||
void IO::AddScriptFunctions()
|
||||
{
|
||||
Script::AddFunction("FileWrite", [] // gsc: FileWrite(<filepath>, <string>, <mode>)
|
||||
{
|
||||
const auto* path = Game::Scr_GetString(0);
|
||||
auto* text = Game::Scr_GetString(1);
|
||||
auto* mode = Game::Scr_GetString(2);
|
||||
|
||||
if (path == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1FileWrite: filepath is not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (text == nullptr || mode == nullptr)
|
||||
{
|
||||
Game::Scr_Error("^1FileWrite: Illegal parameters!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < ARRAYSIZE(QueryStrings); ++i)
|
||||
{
|
||||
if (std::strstr(path, QueryStrings[i]) != nullptr)
|
||||
{
|
||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "FileWrite: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != "append"s && mode != "write"s)
|
||||
{
|
||||
Logger::Warning(Game::CON_CHANNEL_SCRIPT, "FileWrite: mode not defined or was wrong, defaulting to 'write'\n");
|
||||
mode = "write";
|
||||
}
|
||||
|
||||
const auto* scriptData = Utils::String::VA("%s/%s", "scriptdata", path);
|
||||
|
||||
if (mode == "write"s)
|
||||
{
|
||||
FileSystem::FileWriter(scriptData).write(text);
|
||||
}
|
||||
else if (mode == "append"s)
|
||||
{
|
||||
FileSystem::FileWriter(scriptData, true).write(text);
|
||||
}
|
||||
});
|
||||
|
||||
Script::AddFunction("FileRead", [] // gsc: FileRead(<filepath>)
|
||||
{
|
||||
const auto* path = Game::Scr_GetString(0);
|
||||
|
||||
if (path == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1FileRead: filepath is not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < ARRAYSIZE(QueryStrings); ++i)
|
||||
{
|
||||
if (std::strstr(path, QueryStrings[i]) != nullptr)
|
||||
{
|
||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "FileRead: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto* scriptData = Utils::String::VA("%s/%s", "scriptdata", path);
|
||||
|
||||
if (!FileSystem::FileReader(scriptData).exists())
|
||||
{
|
||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "FileRead: file '{}' not found!\n", scriptData);
|
||||
return;
|
||||
}
|
||||
|
||||
Game::Scr_AddString(FileSystem::FileReader(scriptData).getBuffer().data());
|
||||
});
|
||||
|
||||
Script::AddFunction("FileExists", [] // gsc: FileExists(<filepath>)
|
||||
{
|
||||
const auto* path = Game::Scr_GetString(0);
|
||||
|
||||
if (path == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1FileExists: filepath is not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < ARRAYSIZE(QueryStrings); ++i)
|
||||
{
|
||||
if (std::strstr(path, QueryStrings[i]) != nullptr)
|
||||
{
|
||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "FileExists: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto* scriptData = Utils::String::VA("%s/%s", "scriptdata", path);
|
||||
|
||||
Game::Scr_AddBool(FileSystem::FileReader(scriptData).exists());
|
||||
});
|
||||
}
|
||||
|
||||
IO::IO()
|
||||
{
|
||||
AddScriptFunctions();
|
||||
}
|
||||
}
|
15
src/Components/Modules/GSC/IO.hpp
Normal file
15
src/Components/Modules/GSC/IO.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class IO : public Component
|
||||
{
|
||||
public:
|
||||
IO();
|
||||
|
||||
private:
|
||||
static const char* QueryStrings[];
|
||||
|
||||
static void AddScriptFunctions();
|
||||
};
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "Script.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
@ -7,7 +8,6 @@ namespace Components
|
||||
std::unordered_map<std::string, Game::BuiltinMethodDef> Script::CustomScrMethods;
|
||||
std::vector<std::string> Script::ScriptNameStack;
|
||||
unsigned short Script::FunctionName;
|
||||
std::unordered_map<std::string, std::string> Script::ScriptStorage;
|
||||
std::unordered_map<int, std::string> Script::ScriptBaseProgramNum;
|
||||
int Script::LastFrameTime = -1;
|
||||
|
||||
@ -416,11 +416,11 @@ namespace Components
|
||||
|
||||
unsigned int Script::SetExpFogStub()
|
||||
{
|
||||
if (Game::Scr_GetNumParam() == 6u)
|
||||
if (Game::Scr_GetNumParam() == 6)
|
||||
{
|
||||
std::memmove(&Game::scrVmPub->top[-4], &Game::scrVmPub->top[-5], sizeof(Game::VariableValue) * 6);
|
||||
Game::scrVmPub->top += 1;
|
||||
Game::scrVmPub->top[-6].type = Game::scrParamType_t::VAR_FLOAT;
|
||||
Game::scrVmPub->top[-6].type = Game::VAR_FLOAT;
|
||||
Game::scrVmPub->top[-6].u.floatValue = 0.0f;
|
||||
|
||||
++Game::scrVmPub->outparamcount;
|
||||
@ -540,7 +540,7 @@ namespace Components
|
||||
{
|
||||
Script::AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(<function>, <function>)
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 2u)
|
||||
if (Game::Scr_GetNumParam() != 2)
|
||||
{
|
||||
Game::Scr_Error("^1ReplaceFunc: Needs two parameters!\n");
|
||||
return;
|
||||
@ -592,78 +592,6 @@ namespace Components
|
||||
}
|
||||
});
|
||||
|
||||
// Script Storage Functions
|
||||
Script::AddFunction("StorageSet", [] // gsc: StorageSet(<str key>, <str data>);
|
||||
{
|
||||
const auto* key = Game::Scr_GetString(0);
|
||||
const auto* value = Game::Scr_GetString(1);
|
||||
|
||||
if (key == nullptr || value == nullptr)
|
||||
{
|
||||
Game::Scr_Error("^1StorageSet: Illegal parameters!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Script::ScriptStorage.insert_or_assign(key, value);
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageRemove", [] // gsc: StorageRemove(<str key>);
|
||||
{
|
||||
const auto* key = Game::Scr_GetString(0);
|
||||
|
||||
if (key == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1StorageRemove: Illegal parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Script::ScriptStorage.contains(key))
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key));
|
||||
return;
|
||||
}
|
||||
|
||||
Script::ScriptStorage.erase(key);
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageGet", [] // gsc: StorageGet(<str key>);
|
||||
{
|
||||
const auto* key = Game::Scr_GetString(0);
|
||||
|
||||
if (key == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1StorageGet: Illegal parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Script::ScriptStorage.contains(key))
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& data = Script::ScriptStorage.at(key);
|
||||
Game::Scr_AddString(data.data());
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageHas", [] // gsc: StorageHas(<str key>);
|
||||
{
|
||||
const auto* key = Game::Scr_GetString(0);
|
||||
|
||||
if (key == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1StorageHas: Illegal parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::Scr_AddBool(Script::ScriptStorage.contains(key));
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageClear", [] // gsc: StorageClear();
|
||||
{
|
||||
Script::ScriptStorage.clear();
|
||||
});
|
||||
|
||||
// PlayerCmd_AreControlsFrozen GSC function from Black Ops 2
|
||||
Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen();
|
||||
{
|
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <Game/Structs.hpp>
|
||||
|
||||
namespace Components
|
||||
{
|
||||
@ -21,7 +20,6 @@ namespace Components
|
||||
static std::unordered_map<std::string, Game::BuiltinMethodDef> CustomScrMethods;
|
||||
static std::vector<std::string> ScriptNameStack;
|
||||
static unsigned short FunctionName;
|
||||
static std::unordered_map<std::string, std::string> ScriptStorage;
|
||||
static std::unordered_map<int, std::string> ScriptBaseProgramNum;
|
||||
static int LastFrameTime;
|
||||
|
@ -1,9 +1,9 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "ScriptExtension.hpp"
|
||||
#include "Script.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
const char* ScriptExtension::QueryStrings[] = { R"(..)", R"(../)", R"(..\)" };
|
||||
|
||||
std::unordered_map<std::uint16_t, Game::ent_field_t> ScriptExtension::CustomEntityFields;
|
||||
std::unordered_map<std::uint16_t, Game::client_fields_s> ScriptExtension::CustomClientFields;
|
||||
|
||||
@ -13,7 +13,7 @@ namespace Components
|
||||
static std::uint16_t fieldOffsetStart = 15; // fields count
|
||||
assert((fieldOffsetStart & Game::ENTFIELD_MASK) == Game::ENTFIELD_ENTITY);
|
||||
|
||||
ScriptExtension::CustomEntityFields[fieldOffsetStart] = {name, fieldOffsetStart, type, setter, getter};
|
||||
CustomEntityFields[fieldOffsetStart] = {name, fieldOffsetStart, type, setter, getter};
|
||||
++fieldOffsetStart;
|
||||
}
|
||||
|
||||
@ -26,20 +26,20 @@ namespace Components
|
||||
const auto offset = fieldOffsetStart | Game::ENTFIELD_CLIENT; // This is how client field's offset is calculated
|
||||
|
||||
// Use 'index' in 'array' as map key. It will be used later in Scr_SetObjectFieldStub
|
||||
ScriptExtension::CustomClientFields[fieldOffsetStart] = {name, offset, type, setter, getter};
|
||||
CustomClientFields[fieldOffsetStart] = {name, offset, type, setter, getter};
|
||||
++fieldOffsetStart;
|
||||
}
|
||||
|
||||
void ScriptExtension::GScr_AddFieldsForEntityStub()
|
||||
{
|
||||
for (const auto& [offset, field] : ScriptExtension::CustomEntityFields)
|
||||
for (const auto& [offset, field] : CustomEntityFields)
|
||||
{
|
||||
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
||||
}
|
||||
|
||||
Utils::Hook::Call<void()>(0x4A7CF0)(); // GScr_AddFieldsForClient
|
||||
|
||||
for (const auto& [offset, field] : ScriptExtension::CustomClientFields)
|
||||
for (const auto& [offset, field] : CustomClientFields)
|
||||
{
|
||||
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
||||
}
|
||||
@ -52,8 +52,8 @@ namespace Components
|
||||
{
|
||||
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
||||
|
||||
const auto got = ScriptExtension::CustomEntityFields.find(entity_offset);
|
||||
if (got != ScriptExtension::CustomEntityFields.end())
|
||||
const auto got =CustomEntityFields.find(entity_offset);
|
||||
if (got != CustomEntityFields.end())
|
||||
{
|
||||
got->second.setter(&Game::g_entities[entnum], offset);
|
||||
return 1;
|
||||
@ -69,8 +69,8 @@ namespace Components
|
||||
{
|
||||
const auto client_offset = static_cast<std::uint16_t>(offset);
|
||||
|
||||
const auto got = ScriptExtension::CustomClientFields.find(client_offset);
|
||||
if (got != ScriptExtension::CustomClientFields.end())
|
||||
const auto got = CustomClientFields.find(client_offset);
|
||||
if (got != CustomClientFields.end())
|
||||
{
|
||||
got->second.setter(client, &got->second);
|
||||
return;
|
||||
@ -89,8 +89,8 @@ namespace Components
|
||||
{
|
||||
const auto client_offset = static_cast<std::uint16_t>(offset & ~Game::ENTFIELD_MASK);
|
||||
|
||||
const auto got = ScriptExtension::CustomClientFields.find(client_offset);
|
||||
if (got != ScriptExtension::CustomClientFields.end())
|
||||
const auto got =CustomClientFields.find(client_offset);
|
||||
if (got != CustomClientFields.end())
|
||||
{
|
||||
// Game functions probably don't ever need to use the reference to client_fields_s...
|
||||
got->second.getter(Game::g_entities[entnum].client, &got->second);
|
||||
@ -102,8 +102,8 @@ namespace Components
|
||||
// Regular entity offsets can be searched directly in our custom handler
|
||||
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
||||
|
||||
const auto got = ScriptExtension::CustomEntityFields.find(entity_offset);
|
||||
if (got != ScriptExtension::CustomEntityFields.end())
|
||||
const auto got = CustomEntityFields.find(entity_offset);
|
||||
if (got != CustomEntityFields.end())
|
||||
{
|
||||
got->second.getter(&Game::g_entities[entnum], offset);
|
||||
return;
|
||||
@ -115,125 +115,6 @@ namespace Components
|
||||
|
||||
void ScriptExtension::AddFunctions()
|
||||
{
|
||||
// File functions
|
||||
Script::AddFunction("FileWrite", [] // gsc: FileWrite(<filepath>, <string>, <mode>)
|
||||
{
|
||||
const auto* path = Game::Scr_GetString(0);
|
||||
auto* text = Game::Scr_GetString(1);
|
||||
auto* mode = Game::Scr_GetString(2);
|
||||
|
||||
if (path == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1FileWrite: filepath is not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (text == nullptr || mode == nullptr)
|
||||
{
|
||||
Game::Scr_Error("^1FileWrite: Illegal parameters!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
|
||||
{
|
||||
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
|
||||
{
|
||||
Logger::Print("^1FileWrite: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != "append"s && mode != "write"s)
|
||||
{
|
||||
Logger::Print("^3FileWrite: mode not defined or was wrong, defaulting to 'write'\n");
|
||||
mode = "write";
|
||||
}
|
||||
|
||||
if (mode == "write"s)
|
||||
{
|
||||
FileSystem::FileWriter(path).write(text);
|
||||
}
|
||||
else if (mode == "append"s)
|
||||
{
|
||||
FileSystem::FileWriter(path, true).write(text);
|
||||
}
|
||||
});
|
||||
|
||||
Script::AddFunction("FileRead", [] // gsc: FileRead(<filepath>)
|
||||
{
|
||||
const auto* path = Game::Scr_GetString(0);
|
||||
|
||||
if (path == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1FileRead: filepath is not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
|
||||
{
|
||||
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
|
||||
{
|
||||
Logger::Print("^1FileRead: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!FileSystem::FileReader(path).exists())
|
||||
{
|
||||
Logger::Print("^1FileRead: file not found!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::Scr_AddString(FileSystem::FileReader(path).getBuffer().data());
|
||||
});
|
||||
|
||||
Script::AddFunction("FileExists", [] // gsc: FileExists(<filepath>)
|
||||
{
|
||||
const auto* path = Game::Scr_GetString(0);
|
||||
|
||||
if (path == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1FileExists: filepath is not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
|
||||
{
|
||||
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
|
||||
{
|
||||
Logger::Print("^1FileExists: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Game::Scr_AddInt(FileSystem::FileReader(path).exists());
|
||||
});
|
||||
|
||||
Script::AddFunction("FileRemove", [] // gsc: FileRemove(<filepath>)
|
||||
{
|
||||
const auto* path = Game::Scr_GetString(0);
|
||||
|
||||
if (path == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1FileRemove: filepath is not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
|
||||
{
|
||||
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
|
||||
{
|
||||
Logger::Print("^1FileRemove: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto p = std::filesystem::path(path);
|
||||
const auto& folder = p.parent_path().string();
|
||||
const auto& file = p.filename().string();
|
||||
Game::Scr_AddInt(FileSystem::DeleteFile(folder, file));
|
||||
});
|
||||
|
||||
// Misc functions
|
||||
Script::AddFunction("ToUpper", [] // gsc: ToUpper(<string>)
|
||||
{
|
||||
@ -311,11 +192,11 @@ namespace Components
|
||||
const auto type = Game::Scr_GetType(0);
|
||||
|
||||
bool result;
|
||||
if (type == Game::scrParamType_t::VAR_POINTER)
|
||||
if (type == Game::VAR_POINTER)
|
||||
{
|
||||
const auto ptr_type = Game::Scr_GetPointerType(0);
|
||||
assert(ptr_type >= Game::FIRST_OBJECT);
|
||||
result = (ptr_type == Game::scrParamType_t::VAR_ARRAY);
|
||||
result = (ptr_type == Game::VAR_ARRAY);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -350,6 +231,18 @@ namespace Components
|
||||
|
||||
Game::Scr_AddInt(client->ping);
|
||||
});
|
||||
|
||||
Script::AddMethod("SetPing", [](Game::scr_entref_t entref) // gsc: self SetPing(<int>)
|
||||
{
|
||||
auto ping = Game::Scr_GetInt(0);
|
||||
|
||||
ping = std::clamp(ping, 0, 999);
|
||||
|
||||
const auto* ent = Game::GetPlayerEntity(entref);
|
||||
auto* client = Script::GetClient(ent);
|
||||
|
||||
client->ping = static_cast<int16_t>(ping);
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptExtension::Scr_TableLookupIStringByRow()
|
||||
@ -378,7 +271,7 @@ namespace Components
|
||||
|
||||
void ScriptExtension::AddEntityFields()
|
||||
{
|
||||
ScriptExtension::AddEntityField("entityflags", Game::fieldtype_t::F_INT,
|
||||
AddEntityField("entityflags", Game::fieldtype_t::F_INT,
|
||||
[](Game::gentity_s* ent, [[maybe_unused]] int offset)
|
||||
{
|
||||
ent->flags = Game::Scr_GetInt(0);
|
||||
@ -391,7 +284,7 @@ namespace Components
|
||||
|
||||
void ScriptExtension::AddClientFields()
|
||||
{
|
||||
ScriptExtension::AddClientField("clientflags", Game::fieldtype_t::F_INT,
|
||||
AddClientField("clientflags", Game::fieldtype_t::F_INT,
|
||||
[](Game::gclient_s* pSelf, [[maybe_unused]] const Game::client_fields_s* pField)
|
||||
{
|
||||
pSelf->flags = Game::Scr_GetInt(0);
|
||||
@ -404,19 +297,19 @@ namespace Components
|
||||
|
||||
ScriptExtension::ScriptExtension()
|
||||
{
|
||||
ScriptExtension::AddFunctions();
|
||||
ScriptExtension::AddMethods();
|
||||
ScriptExtension::AddEntityFields();
|
||||
ScriptExtension::AddClientFields();
|
||||
AddFunctions();
|
||||
AddMethods();
|
||||
AddEntityFields();
|
||||
AddClientFields();
|
||||
|
||||
// Correct builtin function pointer
|
||||
Utils::Hook::Set<void(*)()>(0x79A90C, ScriptExtension::Scr_TableLookupIStringByRow);
|
||||
Utils::Hook::Set<Game::BuiltinFunction>(0x79A90C, Scr_TableLookupIStringByRow);
|
||||
|
||||
Utils::Hook(0x4EC721, ScriptExtension::GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity
|
||||
Utils::Hook(0x4EC721, GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity
|
||||
|
||||
Utils::Hook(0x41BED2, ScriptExtension::Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue
|
||||
Utils::Hook(0x5FBF01, ScriptExtension::Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField
|
||||
Utils::Hook(0x4FF413, ScriptExtension::Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField
|
||||
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
|
||||
|
||||
// Fix format string in Scr_RandomFloatRange
|
||||
Utils::Hook::Set<const char*>(0x5F10C6, "Scr_RandomFloatRange parms: %f %f ");
|
@ -11,8 +11,6 @@ namespace Components
|
||||
static void AddClientField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackClient& setter, const Game::ScriptCallbackClient& getter);
|
||||
|
||||
private:
|
||||
static const char* QueryStrings[];
|
||||
|
||||
static std::unordered_map<std::uint16_t, Game::ent_field_t> CustomEntityFields;
|
||||
static std::unordered_map<std::uint16_t, Game::client_fields_s> CustomClientFields;
|
||||
|
101
src/Components/Modules/GSC/ScriptStorage.cpp
Normal file
101
src/Components/Modules/GSC/ScriptStorage.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "ScriptStorage.hpp"
|
||||
#include "Script.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::unordered_map<std::string, std::string> ScriptStorage::Data;
|
||||
|
||||
void ScriptStorage::AddScriptFunctions()
|
||||
{
|
||||
Script::AddFunction("StorageSet", [] // gsc: StorageSet(<str key>, <str data>);
|
||||
{
|
||||
const auto* key = Game::Scr_GetString(0);
|
||||
const auto* value = Game::Scr_GetString(1);
|
||||
|
||||
if (key == nullptr || value == nullptr)
|
||||
{
|
||||
Game::Scr_Error("^1StorageSet: Illegal parameters!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Data.insert_or_assign(key, value);
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageRemove", [] // gsc: StorageRemove(<str key>);
|
||||
{
|
||||
const auto* key = Game::Scr_GetString(0);
|
||||
|
||||
if (key == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1StorageRemove: Illegal parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Data.contains(key))
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key));
|
||||
return;
|
||||
}
|
||||
|
||||
Data.erase(key);
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageGet", [] // gsc: StorageGet(<str key>);
|
||||
{
|
||||
const auto* key = Game::Scr_GetString(0);
|
||||
|
||||
if (key == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1StorageGet: Illegal parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Data.contains(key))
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& data = Data.at(key);
|
||||
Game::Scr_AddString(data.data());
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageHas", [] // gsc: StorageHas(<str key>);
|
||||
{
|
||||
const auto* key = Game::Scr_GetString(0);
|
||||
|
||||
if (key == nullptr)
|
||||
{
|
||||
Game::Scr_ParamError(0, "^1StorageHas: Illegal parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::Scr_AddBool(Data.contains(key));
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageDump", [] // gsc: StorageDump();
|
||||
{
|
||||
if (Data.empty())
|
||||
{
|
||||
Game::Scr_Error("^1StorageDump: ScriptStorage is empty!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const json11::Json json = Data;
|
||||
|
||||
FileSystem::FileWriter("scriptdata/scriptstorage.json").write(json.dump());
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageClear", [] // gsc: StorageClear();
|
||||
{
|
||||
Data.clear();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ScriptStorage::ScriptStorage()
|
||||
{
|
||||
AddScriptFunctions();
|
||||
}
|
||||
}
|
15
src/Components/Modules/GSC/ScriptStorage.hpp
Normal file
15
src/Components/Modules/GSC/ScriptStorage.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class ScriptStorage : public Component
|
||||
{
|
||||
public:
|
||||
ScriptStorage();
|
||||
|
||||
private:
|
||||
static std::unordered_map<std::string, std::string> Data;
|
||||
|
||||
static void AddScriptFunctions();
|
||||
};
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "GSC/Script.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "GSC/Script.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
|
@ -13,7 +13,6 @@ namespace Components
|
||||
Dvar::Var ZoneBuilder::PreferDiskAssetsDvar;
|
||||
|
||||
ZoneBuilder::Zone::Zone(const std::string& name) : indexStart(0), externalSize(0),
|
||||
|
||||
// Reserve 100MB by default.
|
||||
// That's totally fine, as the dedi doesn't load images and therefore doesn't need much memory.
|
||||
// That way we can be sure it won't need to reallocate memory.
|
||||
@ -21,11 +20,8 @@ namespace Components
|
||||
// Well, decompressed maps can get way larger than 100MB, so let's increase that.
|
||||
buffer(0xC800000),
|
||||
zoneName(name), dataMap("zone_source/" + name + ".csv"), branding{ nullptr }, assetDepth(0)
|
||||
{}
|
||||
|
||||
ZoneBuilder::Zone::Zone() : indexStart(0), externalSize(0), buffer(0xC800000), zoneName("null_zone"),
|
||||
dataMap(), branding{ nullptr }, assetDepth(0)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
ZoneBuilder::Zone::~Zone()
|
||||
{
|
||||
@ -124,7 +120,7 @@ namespace Components
|
||||
{
|
||||
Logger::Print("Loading required FastFiles...\n");
|
||||
|
||||
for (int i = 0; i < this->dataMap.getRows(); ++i)
|
||||
for (std::size_t i = 0; i < this->dataMap.getRows(); ++i)
|
||||
{
|
||||
if (this->dataMap.getElementAt(i, 0) == "require")
|
||||
{
|
||||
@ -149,7 +145,7 @@ namespace Components
|
||||
|
||||
bool ZoneBuilder::Zone::loadAssets()
|
||||
{
|
||||
for (int i = 0; i < this->dataMap.getRows(); ++i)
|
||||
for (std::size_t i = 0; i < this->dataMap.getRows(); ++i)
|
||||
{
|
||||
if (this->dataMap.getElementAt(i, 0) != "require")
|
||||
{
|
||||
|
@ -32,7 +32,6 @@ namespace Components
|
||||
};
|
||||
|
||||
Zone(const std::string& zoneName);
|
||||
Zone();
|
||||
~Zone();
|
||||
|
||||
void build();
|
||||
|
@ -2,36 +2,21 @@
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
CSV::CSV(const std::string& file, bool isFile, bool allowComments)
|
||||
CSV::CSV(const std::string& file, const bool isFile, const bool allowComments)
|
||||
{
|
||||
this->parse(file, isFile, allowComments);
|
||||
}
|
||||
|
||||
CSV::~CSV()
|
||||
std::size_t CSV::getRows() const
|
||||
{
|
||||
this->dataMap.clear();
|
||||
return this->dataMap_.size();
|
||||
}
|
||||
|
||||
int CSV::getRows()
|
||||
std::size_t CSV::getColumns() const
|
||||
{
|
||||
return this->dataMap.size();
|
||||
}
|
||||
std::size_t count = 0;
|
||||
|
||||
int CSV::getColumns(size_t row)
|
||||
{
|
||||
if (this->dataMap.size() > row)
|
||||
{
|
||||
return this->dataMap[row].size();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CSV::getColumns()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < this->getRows(); ++i)
|
||||
for (std::size_t i = 0; i < this->getRows(); ++i)
|
||||
{
|
||||
count = std::max(this->getColumns(i), count);
|
||||
}
|
||||
@ -39,11 +24,21 @@ namespace Utils
|
||||
return count;
|
||||
}
|
||||
|
||||
std::string CSV::getElementAt(size_t row, size_t column)
|
||||
std::size_t CSV::getColumns(const std::size_t row) const
|
||||
{
|
||||
if (this->dataMap.size() > row)
|
||||
if (this->dataMap_.size() > row)
|
||||
{
|
||||
auto _row = this->dataMap[row];
|
||||
return this->dataMap_[row].size();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string CSV::getElementAt(const std::size_t row, const std::size_t column) const
|
||||
{
|
||||
if (this->dataMap_.size() > row)
|
||||
{
|
||||
auto& _row = this->dataMap_[row];
|
||||
|
||||
if (_row.size() > column)
|
||||
{
|
||||
@ -51,18 +46,27 @@ namespace Utils
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
return {};
|
||||
}
|
||||
|
||||
void CSV::parse(const std::string& file, bool isFile, bool allowComments)
|
||||
bool CSV::isValid() const
|
||||
{
|
||||
return this->valid_;
|
||||
}
|
||||
|
||||
void CSV::parse(const std::string& file, const bool isFile, const bool allowComments)
|
||||
{
|
||||
std::string buffer;
|
||||
|
||||
if (isFile)
|
||||
{
|
||||
if (!Utils::IO::FileExists(file)) return;
|
||||
buffer = Utils::IO::ReadFile(file);
|
||||
this->valid = true;
|
||||
if (!IO::FileExists(file))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
buffer = IO::ReadFile(file);
|
||||
this->valid_ = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -71,7 +75,7 @@ namespace Utils
|
||||
|
||||
if (!buffer.empty())
|
||||
{
|
||||
auto rows = Utils::String::Split(buffer, '\n');
|
||||
const auto rows = String::Split(buffer, '\n');
|
||||
|
||||
for (auto& row : rows)
|
||||
{
|
||||
@ -80,14 +84,14 @@ namespace Utils
|
||||
}
|
||||
}
|
||||
|
||||
void CSV::parseRow(const std::string& row, bool allowComments)
|
||||
void CSV::parseRow(const std::string& row, const bool allowComments)
|
||||
{
|
||||
bool isString = false;
|
||||
std::string element;
|
||||
std::vector<std::string> _row;
|
||||
char tempStr = 0;
|
||||
|
||||
for (unsigned int i = 0; i < row.size(); ++i)
|
||||
for (std::size_t i = 0; i < row.size(); ++i)
|
||||
{
|
||||
if (row[i] == ',' && !isString) // Flush entry
|
||||
{
|
||||
@ -95,25 +99,29 @@ namespace Utils
|
||||
element.clear();
|
||||
continue;
|
||||
}
|
||||
else if (row[i] == '"') // Start/Terminate string
|
||||
|
||||
if (row[i] == '"') // Start/Terminate string
|
||||
{
|
||||
isString = !isString;
|
||||
continue;
|
||||
}
|
||||
else if (i < (row.size() - 1) && row[i] == '\\' &&row[i + 1] == '"' && isString) // Handle quotes in strings as \"
|
||||
|
||||
if (i < (row.size() - 1) && row[i] == '\\' &&row[i + 1] == '"' && isString) // Handle quotes in strings as \"
|
||||
{
|
||||
tempStr = '"';
|
||||
++i;
|
||||
}
|
||||
|
||||
else if (!isString && (row[i] == '\n' || row[i] == '\x0D' || row[i] == '\x0A' || row[i] == '\t'))
|
||||
{
|
||||
//++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
else if (!isString && (row[i] == '#' || (row[i] == '/' && (i + 1) < row.size() && row[i + 1] == '/') ) && allowComments) // Skip comments. I know CSVs usually don't have comments, but in this case it's useful
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
tempStr = row[i];
|
||||
@ -125,11 +133,11 @@ namespace Utils
|
||||
// Push last element
|
||||
_row.push_back(element);
|
||||
|
||||
if (_row.size() == 0 || (_row.size() == 1 && !_row[0].size())) // Skip empty rows
|
||||
if (_row.empty() || (_row.size() == 1 && _row[0].empty())) // Skip empty rows
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->dataMap.push_back(_row);
|
||||
this->dataMap_.push_back(_row);
|
||||
}
|
||||
}
|
||||
|
@ -5,21 +5,19 @@ namespace Utils
|
||||
class CSV
|
||||
{
|
||||
public:
|
||||
CSV() { }
|
||||
CSV(const std::string& file, bool isFile = true, bool allowComments = true);
|
||||
~CSV();
|
||||
|
||||
int getRows();
|
||||
int getColumns();
|
||||
int getColumns(size_t row);
|
||||
[[nodiscard]] std::size_t getRows() const;
|
||||
[[nodiscard]] std::size_t getColumns() const;
|
||||
[[nodiscard]] std::size_t getColumns(std::size_t row) const;
|
||||
|
||||
std::string getElementAt(size_t row, size_t column);
|
||||
[[nodiscard]] std::string getElementAt(std::size_t row, std::size_t column) const;
|
||||
|
||||
bool isValid() { return this->valid; }
|
||||
[[nodiscard]] bool isValid() const;
|
||||
|
||||
private:
|
||||
bool valid = false;
|
||||
std::vector<std::vector<std::string>> dataMap;
|
||||
bool valid_ = false;
|
||||
std::vector<std::vector<std::string>> dataMap_;
|
||||
|
||||
void parse(const std::string& file, bool isFile = true, bool allowComments = true);
|
||||
void parseRow(const std::string& row, bool allowComments = true);
|
||||
|
Loading…
Reference in New Issue
Block a user