Merge pull request #322 from diamante0018/csv-gsc
[Script] Re-work some aspects
This commit is contained in:
commit
87a548f011
@ -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. |
|
| `-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. |
|
| `-console` | Allow the game to display its own separate interactive console window. |
|
||||||
| `-dedicated` | Starts the game as a headless dedicated server. |
|
| `-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. |
|
| `-bigminidumps` | Include all code sections from loaded modules in the dump. |
|
||||||
| `-reallybigminidumps` | Include data sections from all 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. |
|
| `-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 Zones());
|
||||||
Loader::Register(new D3D9Ex());
|
Loader::Register(new D3D9Ex());
|
||||||
Loader::Register(new Logger());
|
Loader::Register(new Logger());
|
||||||
Loader::Register(new Script());
|
|
||||||
Loader::Register(new Weapon());
|
Loader::Register(new Weapon());
|
||||||
Loader::Register(new Window());
|
Loader::Register(new Window());
|
||||||
Loader::Register(new Command());
|
Loader::Register(new Command());
|
||||||
@ -105,7 +104,6 @@ namespace Components
|
|||||||
Loader::Register(new Elevators());
|
Loader::Register(new Elevators());
|
||||||
Loader::Register(new ClientCommand());
|
Loader::Register(new ClientCommand());
|
||||||
Loader::Register(new VisionFile());
|
Loader::Register(new VisionFile());
|
||||||
Loader::Register(new ScriptExtension());
|
|
||||||
Loader::Register(new Branding());
|
Loader::Register(new Branding());
|
||||||
Loader::Register(new Debug());
|
Loader::Register(new Debug());
|
||||||
Loader::Register(new RawMouse());
|
Loader::Register(new RawMouse());
|
||||||
@ -115,6 +113,8 @@ namespace Components
|
|||||||
Loader::Register(new UserInfo());
|
Loader::Register(new UserInfo());
|
||||||
Loader::Register(new Events());
|
Loader::Register(new Events());
|
||||||
|
|
||||||
|
Loader::Register(new GSC());
|
||||||
|
|
||||||
Loader::Pregame = false;
|
Loader::Pregame = false;
|
||||||
|
|
||||||
// Make sure preDestroy is called when the game shuts down
|
// Make sure preDestroy is called when the game shuts down
|
||||||
|
@ -74,7 +74,6 @@ namespace Components
|
|||||||
#include "Modules/Toast.hpp"
|
#include "Modules/Toast.hpp"
|
||||||
#include "Modules/Zones.hpp"
|
#include "Modules/Zones.hpp"
|
||||||
#include "Modules/D3D9Ex.hpp"
|
#include "Modules/D3D9Ex.hpp"
|
||||||
#include "Modules/Script.hpp"
|
|
||||||
#include "Modules/Weapon.hpp"
|
#include "Modules/Weapon.hpp"
|
||||||
#include "Modules/Window.hpp"
|
#include "Modules/Window.hpp"
|
||||||
#include "Modules/Command.hpp"
|
#include "Modules/Command.hpp"
|
||||||
@ -136,7 +135,6 @@ namespace Components
|
|||||||
#include "Modules/ClientCommand.hpp"
|
#include "Modules/ClientCommand.hpp"
|
||||||
#include "Modules/VisionFile.hpp"
|
#include "Modules/VisionFile.hpp"
|
||||||
#include "Modules/Gamepad.hpp"
|
#include "Modules/Gamepad.hpp"
|
||||||
#include "Modules/ScriptExtension.hpp"
|
|
||||||
#include "Modules/Branding.hpp"
|
#include "Modules/Branding.hpp"
|
||||||
#include "Modules/Debug.hpp"
|
#include "Modules/Debug.hpp"
|
||||||
#include "Modules/RawMouse.hpp"
|
#include "Modules/RawMouse.hpp"
|
||||||
@ -145,3 +143,5 @@ namespace Components
|
|||||||
#include "Modules/Ceg.hpp"
|
#include "Modules/Ceg.hpp"
|
||||||
#include "Modules/UserInfo.hpp"
|
#include "Modules/UserInfo.hpp"
|
||||||
#include "Modules/Events.hpp"
|
#include "Modules/Events.hpp"
|
||||||
|
|
||||||
|
#include "Modules/GSC/GSC.hpp"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -121,24 +122,6 @@ namespace Components
|
|||||||
Script::AddMethod("IsBot", Bots::GScr_isTestClient); // Usage: self IsBot();
|
Script::AddMethod("IsBot", Bots::GScr_isTestClient); // Usage: self IsBot();
|
||||||
Script::AddMethod("IsTestClient", Bots::GScr_isTestClient); // Usage: self IsTestClient();
|
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();
|
Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: <bot> BotStop();
|
||||||
{
|
{
|
||||||
const auto* ent = Game::GetPlayerEntity(entref);
|
const auto* ent = Game::GetPlayerEntity(entref);
|
||||||
@ -201,7 +184,7 @@ namespace Components
|
|||||||
return;
|
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)
|
if (Utils::String::ToLower(&action[1]) != BotActions[i].action)
|
||||||
continue;
|
continue;
|
||||||
@ -331,9 +314,9 @@ namespace Components
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Zero the bot command array
|
// 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
|
g_botai[i].weapon = 1; // Prevent the bots from defaulting to the 'none' weapon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -968,9 +969,6 @@ namespace Components
|
|||||||
|
|
||||||
Script::AddFunction("HttpGet", []
|
Script::AddFunction("HttpGet", []
|
||||||
{
|
{
|
||||||
if (!Flags::HasFlag("scriptablehttp"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto* url = Game::Scr_GetString(0);
|
const auto* url = Game::Scr_GetString(0);
|
||||||
|
|
||||||
if (url == nullptr)
|
if (url == nullptr)
|
||||||
@ -989,9 +987,6 @@ namespace Components
|
|||||||
|
|
||||||
Script::AddFunction("HttpCancel", []
|
Script::AddFunction("HttpCancel", []
|
||||||
{
|
{
|
||||||
if (!Flags::HasFlag("scriptablehttp"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto object = Game::Scr_GetObject(0);
|
const auto object = Game::Scr_GetObject(0);
|
||||||
for (const auto& download : Download::ScriptDownloads)
|
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 <STDInclude.hpp>
|
||||||
|
#include "Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -7,7 +8,6 @@ namespace Components
|
|||||||
std::unordered_map<std::string, Game::BuiltinMethodDef> Script::CustomScrMethods;
|
std::unordered_map<std::string, Game::BuiltinMethodDef> Script::CustomScrMethods;
|
||||||
std::vector<std::string> Script::ScriptNameStack;
|
std::vector<std::string> Script::ScriptNameStack;
|
||||||
unsigned short Script::FunctionName;
|
unsigned short Script::FunctionName;
|
||||||
std::unordered_map<std::string, std::string> Script::ScriptStorage;
|
|
||||||
std::unordered_map<int, std::string> Script::ScriptBaseProgramNum;
|
std::unordered_map<int, std::string> Script::ScriptBaseProgramNum;
|
||||||
int Script::LastFrameTime = -1;
|
int Script::LastFrameTime = -1;
|
||||||
|
|
||||||
@ -416,11 +416,11 @@ namespace Components
|
|||||||
|
|
||||||
unsigned int Script::SetExpFogStub()
|
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);
|
std::memmove(&Game::scrVmPub->top[-4], &Game::scrVmPub->top[-5], sizeof(Game::VariableValue) * 6);
|
||||||
Game::scrVmPub->top += 1;
|
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->top[-6].u.floatValue = 0.0f;
|
||||||
|
|
||||||
++Game::scrVmPub->outparamcount;
|
++Game::scrVmPub->outparamcount;
|
||||||
@ -540,7 +540,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Script::AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(<function>, <function>)
|
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");
|
Game::Scr_Error("^1ReplaceFunc: Needs two parameters!\n");
|
||||||
return;
|
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
|
// PlayerCmd_AreControlsFrozen GSC function from Black Ops 2
|
||||||
Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen();
|
Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen();
|
||||||
{
|
{
|
@ -1,5 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Game/Structs.hpp>
|
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -21,7 +20,6 @@ namespace Components
|
|||||||
static std::unordered_map<std::string, Game::BuiltinMethodDef> CustomScrMethods;
|
static std::unordered_map<std::string, Game::BuiltinMethodDef> CustomScrMethods;
|
||||||
static std::vector<std::string> ScriptNameStack;
|
static std::vector<std::string> ScriptNameStack;
|
||||||
static unsigned short FunctionName;
|
static unsigned short FunctionName;
|
||||||
static std::unordered_map<std::string, std::string> ScriptStorage;
|
|
||||||
static std::unordered_map<int, std::string> ScriptBaseProgramNum;
|
static std::unordered_map<int, std::string> ScriptBaseProgramNum;
|
||||||
static int LastFrameTime;
|
static int LastFrameTime;
|
||||||
|
|
@ -1,9 +1,9 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "ScriptExtension.hpp"
|
||||||
|
#include "Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
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::ent_field_t> ScriptExtension::CustomEntityFields;
|
||||||
std::unordered_map<std::uint16_t, Game::client_fields_s> ScriptExtension::CustomClientFields;
|
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
|
static std::uint16_t fieldOffsetStart = 15; // fields count
|
||||||
assert((fieldOffsetStart & Game::ENTFIELD_MASK) == Game::ENTFIELD_ENTITY);
|
assert((fieldOffsetStart & Game::ENTFIELD_MASK) == Game::ENTFIELD_ENTITY);
|
||||||
|
|
||||||
ScriptExtension::CustomEntityFields[fieldOffsetStart] = {name, fieldOffsetStart, type, setter, getter};
|
CustomEntityFields[fieldOffsetStart] = {name, fieldOffsetStart, type, setter, getter};
|
||||||
++fieldOffsetStart;
|
++fieldOffsetStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,20 +26,20 @@ namespace Components
|
|||||||
const auto offset = fieldOffsetStart | Game::ENTFIELD_CLIENT; // This is how client field's offset is calculated
|
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
|
// 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;
|
++fieldOffsetStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptExtension::GScr_AddFieldsForEntityStub()
|
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);
|
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Hook::Call<void()>(0x4A7CF0)(); // GScr_AddFieldsForClient
|
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);
|
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 entity_offset = static_cast<std::uint16_t>(offset);
|
||||||
|
|
||||||
const auto got = ScriptExtension::CustomEntityFields.find(entity_offset);
|
const auto got =CustomEntityFields.find(entity_offset);
|
||||||
if (got != ScriptExtension::CustomEntityFields.end())
|
if (got != CustomEntityFields.end())
|
||||||
{
|
{
|
||||||
got->second.setter(&Game::g_entities[entnum], offset);
|
got->second.setter(&Game::g_entities[entnum], offset);
|
||||||
return 1;
|
return 1;
|
||||||
@ -69,8 +69,8 @@ namespace Components
|
|||||||
{
|
{
|
||||||
const auto client_offset = static_cast<std::uint16_t>(offset);
|
const auto client_offset = static_cast<std::uint16_t>(offset);
|
||||||
|
|
||||||
const auto got = ScriptExtension::CustomClientFields.find(client_offset);
|
const auto got = CustomClientFields.find(client_offset);
|
||||||
if (got != ScriptExtension::CustomClientFields.end())
|
if (got != CustomClientFields.end())
|
||||||
{
|
{
|
||||||
got->second.setter(client, &got->second);
|
got->second.setter(client, &got->second);
|
||||||
return;
|
return;
|
||||||
@ -89,8 +89,8 @@ namespace Components
|
|||||||
{
|
{
|
||||||
const auto client_offset = static_cast<std::uint16_t>(offset & ~Game::ENTFIELD_MASK);
|
const auto client_offset = static_cast<std::uint16_t>(offset & ~Game::ENTFIELD_MASK);
|
||||||
|
|
||||||
const auto got = ScriptExtension::CustomClientFields.find(client_offset);
|
const auto got =CustomClientFields.find(client_offset);
|
||||||
if (got != ScriptExtension::CustomClientFields.end())
|
if (got != CustomClientFields.end())
|
||||||
{
|
{
|
||||||
// Game functions probably don't ever need to use the reference to client_fields_s...
|
// 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);
|
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
|
// Regular entity offsets can be searched directly in our custom handler
|
||||||
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
||||||
|
|
||||||
const auto got = ScriptExtension::CustomEntityFields.find(entity_offset);
|
const auto got = CustomEntityFields.find(entity_offset);
|
||||||
if (got != ScriptExtension::CustomEntityFields.end())
|
if (got != CustomEntityFields.end())
|
||||||
{
|
{
|
||||||
got->second.getter(&Game::g_entities[entnum], offset);
|
got->second.getter(&Game::g_entities[entnum], offset);
|
||||||
return;
|
return;
|
||||||
@ -115,125 +115,6 @@ namespace Components
|
|||||||
|
|
||||||
void ScriptExtension::AddFunctions()
|
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
|
// Misc functions
|
||||||
Script::AddFunction("ToUpper", [] // gsc: ToUpper(<string>)
|
Script::AddFunction("ToUpper", [] // gsc: ToUpper(<string>)
|
||||||
{
|
{
|
||||||
@ -311,11 +192,11 @@ namespace Components
|
|||||||
const auto type = Game::Scr_GetType(0);
|
const auto type = Game::Scr_GetType(0);
|
||||||
|
|
||||||
bool result;
|
bool result;
|
||||||
if (type == Game::scrParamType_t::VAR_POINTER)
|
if (type == Game::VAR_POINTER)
|
||||||
{
|
{
|
||||||
const auto ptr_type = Game::Scr_GetPointerType(0);
|
const auto ptr_type = Game::Scr_GetPointerType(0);
|
||||||
assert(ptr_type >= Game::FIRST_OBJECT);
|
assert(ptr_type >= Game::FIRST_OBJECT);
|
||||||
result = (ptr_type == Game::scrParamType_t::VAR_ARRAY);
|
result = (ptr_type == Game::VAR_ARRAY);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -350,6 +231,18 @@ namespace Components
|
|||||||
|
|
||||||
Game::Scr_AddInt(client->ping);
|
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()
|
void ScriptExtension::Scr_TableLookupIStringByRow()
|
||||||
@ -378,7 +271,7 @@ namespace Components
|
|||||||
|
|
||||||
void ScriptExtension::AddEntityFields()
|
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)
|
[](Game::gentity_s* ent, [[maybe_unused]] int offset)
|
||||||
{
|
{
|
||||||
ent->flags = Game::Scr_GetInt(0);
|
ent->flags = Game::Scr_GetInt(0);
|
||||||
@ -391,7 +284,7 @@ namespace Components
|
|||||||
|
|
||||||
void ScriptExtension::AddClientFields()
|
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)
|
[](Game::gclient_s* pSelf, [[maybe_unused]] const Game::client_fields_s* pField)
|
||||||
{
|
{
|
||||||
pSelf->flags = Game::Scr_GetInt(0);
|
pSelf->flags = Game::Scr_GetInt(0);
|
||||||
@ -404,19 +297,19 @@ namespace Components
|
|||||||
|
|
||||||
ScriptExtension::ScriptExtension()
|
ScriptExtension::ScriptExtension()
|
||||||
{
|
{
|
||||||
ScriptExtension::AddFunctions();
|
AddFunctions();
|
||||||
ScriptExtension::AddMethods();
|
AddMethods();
|
||||||
ScriptExtension::AddEntityFields();
|
AddEntityFields();
|
||||||
ScriptExtension::AddClientFields();
|
AddClientFields();
|
||||||
|
|
||||||
// Correct builtin function pointer
|
// 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(0x41BED2, Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue
|
||||||
Utils::Hook(0x5FBF01, ScriptExtension::Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField
|
Utils::Hook(0x5FBF01, Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField
|
||||||
Utils::Hook(0x4FF413, ScriptExtension::Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField
|
Utils::Hook(0x4FF413, Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField
|
||||||
|
|
||||||
// Fix format string in Scr_RandomFloatRange
|
// Fix format string in Scr_RandomFloatRange
|
||||||
Utils::Hook::Set<const char*>(0x5F10C6, "Scr_RandomFloatRange parms: %f %f ");
|
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);
|
static void AddClientField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackClient& setter, const Game::ScriptCallbackClient& getter);
|
||||||
|
|
||||||
private:
|
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::ent_field_t> CustomEntityFields;
|
||||||
static std::unordered_map<std::uint16_t, Game::client_fields_s> CustomClientFields;
|
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 <STDInclude.hpp>
|
||||||
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,6 @@ namespace Components
|
|||||||
Dvar::Var ZoneBuilder::PreferDiskAssetsDvar;
|
Dvar::Var ZoneBuilder::PreferDiskAssetsDvar;
|
||||||
|
|
||||||
ZoneBuilder::Zone::Zone(const std::string& name) : indexStart(0), externalSize(0),
|
ZoneBuilder::Zone::Zone(const std::string& name) : indexStart(0), externalSize(0),
|
||||||
|
|
||||||
// Reserve 100MB by default.
|
// Reserve 100MB by default.
|
||||||
// That's totally fine, as the dedi doesn't load images and therefore doesn't need much memory.
|
// 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.
|
// 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.
|
// Well, decompressed maps can get way larger than 100MB, so let's increase that.
|
||||||
buffer(0xC800000),
|
buffer(0xC800000),
|
||||||
zoneName(name), dataMap("zone_source/" + name + ".csv"), branding{ nullptr }, assetDepth(0)
|
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()
|
ZoneBuilder::Zone::~Zone()
|
||||||
{
|
{
|
||||||
@ -124,7 +120,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Logger::Print("Loading required FastFiles...\n");
|
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")
|
if (this->dataMap.getElementAt(i, 0) == "require")
|
||||||
{
|
{
|
||||||
@ -149,7 +145,7 @@ namespace Components
|
|||||||
|
|
||||||
bool ZoneBuilder::Zone::loadAssets()
|
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")
|
if (this->dataMap.getElementAt(i, 0) != "require")
|
||||||
{
|
{
|
||||||
|
@ -32,7 +32,6 @@ namespace Components
|
|||||||
};
|
};
|
||||||
|
|
||||||
Zone(const std::string& zoneName);
|
Zone(const std::string& zoneName);
|
||||||
Zone();
|
|
||||||
~Zone();
|
~Zone();
|
||||||
|
|
||||||
void build();
|
void build();
|
||||||
|
@ -2,36 +2,21 @@
|
|||||||
|
|
||||||
namespace Utils
|
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);
|
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)
|
for (std::size_t i = 0; i < this->getRows(); ++i)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
count = std::max(this->getColumns(i), count);
|
count = std::max(this->getColumns(i), count);
|
||||||
}
|
}
|
||||||
@ -39,11 +24,21 @@ namespace Utils
|
|||||||
return count;
|
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)
|
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;
|
std::string buffer;
|
||||||
|
|
||||||
if (isFile)
|
if (isFile)
|
||||||
{
|
{
|
||||||
if (!Utils::IO::FileExists(file)) return;
|
if (!IO::FileExists(file))
|
||||||
buffer = Utils::IO::ReadFile(file);
|
{
|
||||||
this->valid = true;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = IO::ReadFile(file);
|
||||||
|
this->valid_ = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -71,7 +75,7 @@ namespace Utils
|
|||||||
|
|
||||||
if (!buffer.empty())
|
if (!buffer.empty())
|
||||||
{
|
{
|
||||||
auto rows = Utils::String::Split(buffer, '\n');
|
const auto rows = String::Split(buffer, '\n');
|
||||||
|
|
||||||
for (auto& row : rows)
|
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;
|
bool isString = false;
|
||||||
std::string element;
|
std::string element;
|
||||||
std::vector<std::string> _row;
|
std::vector<std::string> _row;
|
||||||
char tempStr = 0;
|
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
|
if (row[i] == ',' && !isString) // Flush entry
|
||||||
{
|
{
|
||||||
@ -95,25 +99,29 @@ namespace Utils
|
|||||||
element.clear();
|
element.clear();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (row[i] == '"') // Start/Terminate string
|
|
||||||
|
if (row[i] == '"') // Start/Terminate string
|
||||||
{
|
{
|
||||||
isString = !isString;
|
isString = !isString;
|
||||||
continue;
|
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 = '"';
|
tempStr = '"';
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (!isString && (row[i] == '\n' || row[i] == '\x0D' || row[i] == '\x0A' || row[i] == '\t'))
|
else if (!isString && (row[i] == '\n' || row[i] == '\x0D' || row[i] == '\x0A' || row[i] == '\t'))
|
||||||
{
|
{
|
||||||
//++i;
|
|
||||||
continue;
|
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
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tempStr = row[i];
|
tempStr = row[i];
|
||||||
@ -125,11 +133,11 @@ namespace Utils
|
|||||||
// Push last element
|
// Push last element
|
||||||
_row.push_back(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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->dataMap.push_back(_row);
|
this->dataMap_.push_back(_row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,21 +5,19 @@ namespace Utils
|
|||||||
class CSV
|
class CSV
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CSV() { }
|
|
||||||
CSV(const std::string& file, bool isFile = true, bool allowComments = true);
|
CSV(const std::string& file, bool isFile = true, bool allowComments = true);
|
||||||
~CSV();
|
|
||||||
|
|
||||||
int getRows();
|
[[nodiscard]] std::size_t getRows() const;
|
||||||
int getColumns();
|
[[nodiscard]] std::size_t getColumns() const;
|
||||||
int getColumns(size_t row);
|
[[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:
|
private:
|
||||||
bool valid = false;
|
bool valid_ = false;
|
||||||
std::vector<std::vector<std::string>> dataMap;
|
std::vector<std::vector<std::string>> dataMap_;
|
||||||
|
|
||||||
void parse(const std::string& file, bool isFile = true, bool allowComments = true);
|
void parse(const std::string& file, bool isFile = true, bool allowComments = true);
|
||||||
void parseRow(const std::string& row, bool allowComments = true);
|
void parseRow(const std::string& row, bool allowComments = true);
|
||||||
|
Loading…
Reference in New Issue
Block a user