scripting additions, changes, and cleanup (#330)
This commit is contained in:
parent
3fb1024e67
commit
8cdb4690c4
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -48,6 +48,10 @@
|
|||||||
[submodule "deps/curl"]
|
[submodule "deps/curl"]
|
||||||
path = deps/curl
|
path = deps/curl
|
||||||
url = https://github.com/curl/curl.git
|
url = https://github.com/curl/curl.git
|
||||||
|
[submodule "deps/json"]
|
||||||
|
path = deps/json
|
||||||
|
url = https://github.com/nlohmann/json.git
|
||||||
[submodule "deps/gsc-tool"]
|
[submodule "deps/gsc-tool"]
|
||||||
path = deps/gsc-tool
|
path = deps/gsc-tool
|
||||||
url = https://github.com/h1-mod/gsc-tool.git
|
url = https://github.com/xensik/gsc-tool.git
|
||||||
|
branch = xlabs
|
||||||
|
2
deps/gsc-tool
vendored
2
deps/gsc-tool
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 378c0d5cd5ac18140bb676eb371d105405dc03fc
|
Subproject commit 842f168a67878f18ea6a42b9d3ce56ff9fafff0d
|
1
deps/json
vendored
Submodule
1
deps/json
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit a3e6e26dc83a726b292f5be0492fcc408663ce55
|
17
deps/premake/json.lua
vendored
Normal file
17
deps/premake/json.lua
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
json = {
|
||||||
|
source = path.join(dependencies.basePath, "json")
|
||||||
|
}
|
||||||
|
|
||||||
|
function json.import()
|
||||||
|
json.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function json.includes()
|
||||||
|
includedirs {path.join(json.source, "single_include/*")}
|
||||||
|
end
|
||||||
|
|
||||||
|
function json.project()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, json)
|
@ -130,12 +130,12 @@ namespace dedicated
|
|||||||
|
|
||||||
void sys_error_stub(const char* msg, ...)
|
void sys_error_stub(const char* msg, ...)
|
||||||
{
|
{
|
||||||
char buffer[2048];
|
char buffer[2048]{};
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, msg);
|
va_start(ap, msg);
|
||||||
|
|
||||||
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap);
|
vsnprintf_s(buffer, _TRUNCATE, msg, ap);
|
||||||
|
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
#include "scripting.hpp"
|
#include "scripting.hpp"
|
||||||
|
|
||||||
#include "game/dvars.hpp"
|
#include "game/dvars.hpp"
|
||||||
#include "game/scripting/functions.hpp"
|
|
||||||
|
|
||||||
#include <xsk/gsc/types.hpp>
|
#include <xsk/gsc/types.hpp>
|
||||||
#include <xsk/gsc/interfaces/compiler.hpp>
|
#include <xsk/gsc/interfaces/compiler.hpp>
|
||||||
@ -72,14 +71,6 @@ namespace gsc
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check back on this to see if there is a property we can distinguish compared to our rawfiles, like compressedLen?
|
|
||||||
// this will filter out the rawfile "gsc" the game zones actually have, this seems to get all of them
|
|
||||||
if (name.starts_with("maps/createfx") || name.starts_with("maps/createart")
|
|
||||||
|| (name.starts_with("maps/mp") && name.ends_with("_fx.gsc")))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto name_str = name.data();
|
const auto name_str = name.data();
|
||||||
|
|
||||||
if (game::DB_XAssetExists(game::ASSET_TYPE_RAWFILE, name_str) &&
|
if (game::DB_XAssetExists(game::ASSET_TYPE_RAWFILE, name_str) &&
|
||||||
@ -107,6 +98,22 @@ namespace gsc
|
|||||||
return loaded_scripts[real_name];
|
return loaded_scripts[real_name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
without this check, gsc rawfiles that a map contains will be compiled. however, these aren't the correct files.
|
||||||
|
each rawfile has a scriptfile counterpart in asset pool that is meant to be used instead.
|
||||||
|
the gsc rawfiles are just leftover from creating the maps.
|
||||||
|
|
||||||
|
(if you are creating a custom map, you can safely have gsc rawfiles without having scriptfile counterparts)
|
||||||
|
*/
|
||||||
|
if (real_name.starts_with("maps/createfx") || real_name.starts_with("maps/createart")
|
||||||
|
|| (real_name.starts_with("maps/mp") && real_name.ends_with("_fx.gsc")))
|
||||||
|
{
|
||||||
|
if (game::DB_XAssetExists(game::ASSET_TYPE_SCRIPTFILE, real_name.data()))
|
||||||
|
{
|
||||||
|
return game::DB_FindXAssetHeader(game::ASSET_TYPE_SCRIPTFILE, real_name.data(), false).scriptfile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string source_buffer{};
|
std::string source_buffer{};
|
||||||
if (!read_scriptfile(real_name + ".gsc", &source_buffer))
|
if (!read_scriptfile(real_name + ".gsc", &source_buffer))
|
||||||
{
|
{
|
||||||
@ -792,7 +799,7 @@ namespace gsc
|
|||||||
|
|
||||||
for (auto i = 0u; i < args.size(); ++i)
|
for (auto i = 0u; i < args.size(); ++i)
|
||||||
{
|
{
|
||||||
const auto str = args[i].as<std::string>();
|
const auto str = args[i].to_string();
|
||||||
buffer.append(str);
|
buffer.append(str);
|
||||||
buffer.append("\t");
|
buffer.append("\t");
|
||||||
}
|
}
|
||||||
@ -865,6 +872,19 @@ namespace gsc
|
|||||||
return scripting::script_value{};
|
return scripting::script_value{};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function::add("getfunction", [](const function_args& args) -> scripting::script_value
|
||||||
|
{
|
||||||
|
const auto filename = args[0].as<std::string>();
|
||||||
|
const auto function = args[1].as<std::string>();
|
||||||
|
|
||||||
|
if (scripting::script_function_table[filename].find(function) != scripting::script_function_table[filename].end())
|
||||||
|
{
|
||||||
|
return scripting::function{scripting::script_function_table[filename][function]};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
|
||||||
scripting::on_shutdown([](int free_scripts)
|
scripting::on_shutdown([](int free_scripts)
|
||||||
{
|
{
|
||||||
if (free_scripts)
|
if (free_scripts)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "game/scripting/script_value.hpp"
|
#include "game/scripting/array.hpp"
|
||||||
|
#include "game/scripting/execution.hpp"
|
||||||
|
#include "game/scripting/function.hpp"
|
||||||
|
|
||||||
namespace gsc
|
namespace gsc
|
||||||
{
|
{
|
||||||
|
@ -50,6 +50,18 @@ namespace io
|
|||||||
|
|
||||||
throw std::runtime_error("fs_game is not properly defined");
|
throw std::runtime_error("fs_game is not properly defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void replace(std::string& str, const std::string& from, const std::string& to)
|
||||||
|
{
|
||||||
|
const auto start_pos = str.find(from);
|
||||||
|
|
||||||
|
if (start_pos == std::string::npos)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
str.replace(start_pos, from.length(), to);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
@ -60,7 +72,7 @@ namespace io
|
|||||||
use_root_folder = utils::flags::has_flag("io_game_dir");
|
use_root_folder = utils::flags::has_flag("io_game_dir");
|
||||||
if (use_root_folder)
|
if (use_root_folder)
|
||||||
{
|
{
|
||||||
console::warn("WARNING: GSC has access to your game folder. To prevent possible malicious code, remove this flag.");
|
console::warn("GSC has access to your game folder. To prevent possible malicious code, remove the '-io_game_dir' launch flag.");
|
||||||
}
|
}
|
||||||
|
|
||||||
gsc::function::add("fileexists", [](const gsc::function_args& args)
|
gsc::function::add("fileexists", [](const gsc::function_args& args)
|
||||||
@ -141,6 +153,19 @@ namespace io
|
|||||||
const auto path = convert_path(args[0].as<std::string>());
|
const auto path = convert_path(args[0].as<std::string>());
|
||||||
return utils::io::remove_file(path);
|
return utils::io::remove_file(path);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gsc::function::add("va", [](const gsc::function_args& args)
|
||||||
|
{
|
||||||
|
auto fmt = args[0].as<std::string>();
|
||||||
|
|
||||||
|
for (auto i = 1u; i < args.size(); i++)
|
||||||
|
{
|
||||||
|
const auto arg = args[i].to_string();
|
||||||
|
replace(fmt, "%s", arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
210
src/client/component/json.cpp
Normal file
210
src/client/component/json.cpp
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
#include "gsc.hpp"
|
||||||
|
#include "json.hpp"
|
||||||
|
|
||||||
|
#include <json.hpp>
|
||||||
|
|
||||||
|
namespace json
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
nlohmann::json gsc_to_json(scripting::script_value _value);
|
||||||
|
|
||||||
|
nlohmann::json entity_to_array(unsigned int id)
|
||||||
|
{
|
||||||
|
scripting::array array(id);
|
||||||
|
nlohmann::json obj;
|
||||||
|
|
||||||
|
auto string_indexed = -1;
|
||||||
|
const auto keys = array.get_keys();
|
||||||
|
for (auto i = 0; i < keys.size(); i++)
|
||||||
|
{
|
||||||
|
const auto is_int = keys[i].is<int>();
|
||||||
|
const auto is_string = keys[i].is<std::string>();
|
||||||
|
|
||||||
|
if (string_indexed == -1)
|
||||||
|
{
|
||||||
|
string_indexed = is_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string_indexed && is_int)
|
||||||
|
{
|
||||||
|
const auto index = keys[i].as<int>();
|
||||||
|
obj[index] = gsc_to_json(array[index]);
|
||||||
|
}
|
||||||
|
else if (string_indexed && is_string)
|
||||||
|
{
|
||||||
|
const auto key = keys[i].as<std::string>();
|
||||||
|
obj.emplace(key, gsc_to_json(array[key]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json vector_to_array(const float* value)
|
||||||
|
{
|
||||||
|
nlohmann::json obj;
|
||||||
|
obj.push_back(value[0]);
|
||||||
|
obj.push_back(value[1]);
|
||||||
|
obj.push_back(value[2]);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlohmann::json gsc_to_json(scripting::script_value _value)
|
||||||
|
{
|
||||||
|
const auto variable = _value.get_raw();
|
||||||
|
const auto value = variable.u;
|
||||||
|
const auto type = variable.type;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case (game::SCRIPT_NONE):
|
||||||
|
return {};
|
||||||
|
case (game::SCRIPT_INTEGER):
|
||||||
|
return value.intValue;
|
||||||
|
case (game::SCRIPT_FLOAT):
|
||||||
|
return value.floatValue;
|
||||||
|
case (game::SCRIPT_STRING):
|
||||||
|
case (game::SCRIPT_ISTRING):
|
||||||
|
return game::SL_ConvertToString(static_cast<game::scr_string_t>(value.stringValue));
|
||||||
|
case (game::SCRIPT_VECTOR):
|
||||||
|
return vector_to_array(value.vectorValue);
|
||||||
|
case (game::SCRIPT_OBJECT):
|
||||||
|
{
|
||||||
|
const auto object_type = game::scr_VarGlob->objectVariableValue[value.uintValue].w.type;
|
||||||
|
|
||||||
|
switch (object_type)
|
||||||
|
{
|
||||||
|
case (game::SCRIPT_STRUCT):
|
||||||
|
return "[struct]";
|
||||||
|
case (game::SCRIPT_ARRAY):
|
||||||
|
return entity_to_array(value.uintValue);
|
||||||
|
default:
|
||||||
|
return "[entity]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case (game::SCRIPT_FUNCTION):
|
||||||
|
return _value.as<scripting::function>().get_name();
|
||||||
|
default:
|
||||||
|
return "[unknown type]";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
scripting::script_value json_to_gsc(nlohmann::json obj)
|
||||||
|
{
|
||||||
|
const auto type = obj.type();
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case (nlohmann::detail::value_t::number_integer):
|
||||||
|
case (nlohmann::detail::value_t::number_unsigned):
|
||||||
|
return obj.get<int>();
|
||||||
|
case (nlohmann::detail::value_t::number_float):
|
||||||
|
return obj.get<float>();
|
||||||
|
case (nlohmann::detail::value_t::string):
|
||||||
|
return obj.get<std::string>();
|
||||||
|
case (nlohmann::detail::value_t::array):
|
||||||
|
{
|
||||||
|
scripting::array array;
|
||||||
|
|
||||||
|
for (const auto& [key, value] : obj.items())
|
||||||
|
{
|
||||||
|
array.push(json_to_gsc(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return array.get_raw();
|
||||||
|
}
|
||||||
|
case (nlohmann::detail::value_t::object):
|
||||||
|
{
|
||||||
|
scripting::array array;
|
||||||
|
|
||||||
|
for (const auto& [key, value] : obj.items())
|
||||||
|
{
|
||||||
|
array[key] = json_to_gsc(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array.get_raw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string gsc_to_string(const scripting::script_value& value)
|
||||||
|
{
|
||||||
|
return gsc_to_json(value).dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
gsc::function::add("array", [](const gsc::function_args& args)
|
||||||
|
{
|
||||||
|
scripting::array array(args.get_raw());
|
||||||
|
return array.get_raw();
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("map", [](const gsc::function_args& args)
|
||||||
|
{
|
||||||
|
scripting::array array;
|
||||||
|
|
||||||
|
for (auto i = 0u; i < args.size(); i += 2)
|
||||||
|
{
|
||||||
|
if (i >= args.size() - 1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto key = args[i].as<std::string>();
|
||||||
|
array[key] = args[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("jsonparse", [](const gsc::function_args& args)
|
||||||
|
{
|
||||||
|
const auto json = args[0].as<std::string>();
|
||||||
|
const auto obj = nlohmann::json::parse(json);
|
||||||
|
return json_to_gsc(obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("jsonserialize", [](const gsc::function_args& args)
|
||||||
|
{
|
||||||
|
const auto value = args[0];
|
||||||
|
auto indent = -1;
|
||||||
|
|
||||||
|
if (args.size() > 1)
|
||||||
|
{
|
||||||
|
indent = args[1].as<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return gsc_to_json(value).dump(indent);
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("jsonprint", [](const gsc::function_args& args) -> scripting::script_value
|
||||||
|
{
|
||||||
|
std::string buffer;
|
||||||
|
|
||||||
|
for (const auto arg : args.get_raw())
|
||||||
|
{
|
||||||
|
buffer.append(gsc_to_string(arg));
|
||||||
|
buffer.append("\t");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s\n", buffer.data());
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(json::component)
|
6
src/client/component/json.hpp
Normal file
6
src/client/component/json.hpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace json
|
||||||
|
{
|
||||||
|
std::string gsc_to_string(const scripting::script_value& _value);
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "gsc.hpp"
|
||||||
#include "logfile.hpp"
|
#include "logfile.hpp"
|
||||||
|
#include "scripting.hpp"
|
||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
#include "game/dvars.hpp"
|
#include "game/dvars.hpp"
|
||||||
@ -34,6 +36,8 @@ namespace logfile
|
|||||||
std::vector<sol::protected_function> player_killed_callbacks;
|
std::vector<sol::protected_function> player_killed_callbacks;
|
||||||
std::vector<sol::protected_function> player_damage_callbacks;
|
std::vector<sol::protected_function> player_damage_callbacks;
|
||||||
|
|
||||||
|
std::vector<scripting::function> say_callbacks;
|
||||||
|
|
||||||
game::dvar_t* logfile;
|
game::dvar_t* logfile;
|
||||||
game::dvar_t* g_log;
|
game::dvar_t* g_log;
|
||||||
|
|
||||||
@ -302,13 +306,22 @@ namespace logfile
|
|||||||
|
|
||||||
game::SV_Cmd_ArgvBuffer(0, cmd, 1024);
|
game::SV_Cmd_ArgvBuffer(0, cmd, 1024);
|
||||||
|
|
||||||
|
auto hidden = false;
|
||||||
if (cmd == "say"s || cmd == "say_team"s)
|
if (cmd == "say"s || cmd == "say_team"s)
|
||||||
{
|
{
|
||||||
auto hidden = false;
|
|
||||||
std::string message(game::ConcatArgs(1));
|
std::string message(game::ConcatArgs(1));
|
||||||
|
message.erase(0, 1);
|
||||||
|
|
||||||
hidden = message[1] == '/';
|
for (const auto& callback : say_callbacks)
|
||||||
message.erase(0, hidden ? 2 : 1);
|
{
|
||||||
|
const auto entity_id = game::Scr_GetEntityId(client_num, 0);
|
||||||
|
const auto result = callback(entity_id, {message, cmd == "say_team"s});
|
||||||
|
|
||||||
|
if (result.is<int>() && !hidden)
|
||||||
|
{
|
||||||
|
hidden = result.as<int>() == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
scheduler::once([cmd, message, self, hidden]()
|
scheduler::once([cmd, message, self, hidden]()
|
||||||
{
|
{
|
||||||
@ -383,6 +396,18 @@ namespace logfile
|
|||||||
g_log = dvars::register_string("g_log", "h1-mod\\logs\\games_mp.log", game::DVAR_FLAG_NONE, "Log file path");
|
g_log = dvars::register_string("g_log", "h1-mod\\logs\\games_mp.log", game::DVAR_FLAG_NONE, "Log file path");
|
||||||
}, scheduler::pipeline::main);
|
}, scheduler::pipeline::main);
|
||||||
g_log_printf_hook.create(game::G_LogPrintf, g_log_printf_stub);
|
g_log_printf_hook.create(game::G_LogPrintf, g_log_printf_stub);
|
||||||
|
|
||||||
|
gsc::function::add("onplayersay", [](const gsc::function_args& args) -> scripting::script_value
|
||||||
|
{
|
||||||
|
const auto function = args[0].as<scripting::function>();
|
||||||
|
say_callbacks.push_back(function);
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
|
||||||
|
scripting::on_shutdown([](int)
|
||||||
|
{
|
||||||
|
say_callbacks.clear();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ namespace logger
|
|||||||
{
|
{
|
||||||
utils::hook::detour com_error_hook;
|
utils::hook::detour com_error_hook;
|
||||||
|
|
||||||
const game::dvar_t* logger_dev = nullptr;
|
game::dvar_t* logger_dev = nullptr;
|
||||||
|
|
||||||
void print_error(const char* msg, ...)
|
void print_error(const char* msg, ...)
|
||||||
{
|
{
|
||||||
|
@ -1,33 +1,27 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
#include "game/game.hpp"
|
|
||||||
#include "game/dvars.hpp"
|
|
||||||
|
|
||||||
#include "game/scripting/entity.hpp"
|
|
||||||
#include "game/scripting/functions.hpp"
|
|
||||||
#include "game/scripting/event.hpp"
|
|
||||||
#include "game/scripting/lua/engine.hpp"
|
|
||||||
#include "game/scripting/execution.hpp"
|
|
||||||
|
|
||||||
#include "console.hpp"
|
|
||||||
#include "gsc.hpp"
|
#include "gsc.hpp"
|
||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
#include "scripting.hpp"
|
#include "scripting.hpp"
|
||||||
|
|
||||||
#include <xsk/gsc/types.hpp>
|
#include "game/game.hpp"
|
||||||
#include <xsk/resolver.hpp>
|
|
||||||
#include <xsk/utils/compression.hpp>
|
#include "game/scripting/event.hpp"
|
||||||
|
#include "game/scripting/execution.hpp"
|
||||||
|
#include "game/scripting/functions.hpp"
|
||||||
|
#include "game/scripting/lua/engine.hpp"
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
#include <utils/io.hpp>
|
|
||||||
#include <utils/string.hpp>
|
|
||||||
|
|
||||||
namespace scripting
|
namespace scripting
|
||||||
{
|
{
|
||||||
std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
|
std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
|
||||||
|
|
||||||
std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
|
std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
|
||||||
std::unordered_map<std::string, std::vector<std::pair<std::string, const char*>>> script_function_table_sort;
|
std::unordered_map<std::string, std::vector<std::pair<std::string, const char*>>> script_function_table_sort;
|
||||||
|
std::unordered_map<const char*, std::pair<std::string, std::string>> script_function_table_rev;
|
||||||
|
|
||||||
utils::concurrency::container<shared_table_t> shared_table;
|
utils::concurrency::container<shared_table_t> shared_table;
|
||||||
|
|
||||||
std::string current_file;
|
std::string current_file;
|
||||||
@ -49,7 +43,7 @@ namespace scripting
|
|||||||
|
|
||||||
utils::hook::detour db_find_xasset_header_hook;
|
utils::hook::detour db_find_xasset_header_hook;
|
||||||
|
|
||||||
std::string current_scriptfile;
|
std::string current_script_file;
|
||||||
unsigned int current_file_id{};
|
unsigned int current_file_id{};
|
||||||
|
|
||||||
game::dvar_t* g_dump_scripts;
|
game::dvar_t* g_dump_scripts;
|
||||||
@ -96,14 +90,11 @@ namespace scripting
|
|||||||
{
|
{
|
||||||
if (!game::VirtualLobby_Loaded())
|
if (!game::VirtualLobby_Loaded())
|
||||||
{
|
{
|
||||||
// init game in game log
|
|
||||||
game::G_LogPrintf("------------------------------------------------------------\n");
|
game::G_LogPrintf("------------------------------------------------------------\n");
|
||||||
game::G_LogPrintf("InitGame\n");
|
game::G_LogPrintf("InitGame\n");
|
||||||
|
|
||||||
// start lua engine
|
|
||||||
lua::engine::start();
|
lua::engine::start();
|
||||||
|
|
||||||
// execute main handles
|
|
||||||
gsc::load_main_handles();
|
gsc::load_main_handles();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +105,6 @@ namespace scripting
|
|||||||
{
|
{
|
||||||
if (!game::VirtualLobby_Loaded())
|
if (!game::VirtualLobby_Loaded())
|
||||||
{
|
{
|
||||||
// execute init handles
|
|
||||||
gsc::load_init_handles();
|
gsc::load_init_handles();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +131,7 @@ namespace scripting
|
|||||||
game::G_LogPrintf("ShutdownGame:\n");
|
game::G_LogPrintf("ShutdownGame:\n");
|
||||||
game::G_LogPrintf("------------------------------------------------------------\n");
|
game::G_LogPrintf("------------------------------------------------------------\n");
|
||||||
|
|
||||||
return g_shutdown_game_hook.invoke<void>(free_scripts);
|
g_shutdown_game_hook.invoke<void>(free_scripts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void scr_add_class_field_stub(unsigned int classnum, game::scr_string_t name, unsigned int canonical_string, unsigned int offset)
|
void scr_add_class_field_stub(unsigned int classnum, game::scr_string_t name, unsigned int canonical_string, unsigned int offset)
|
||||||
@ -158,7 +148,7 @@ namespace scripting
|
|||||||
|
|
||||||
void process_script_stub(const char* filename)
|
void process_script_stub(const char* filename)
|
||||||
{
|
{
|
||||||
current_scriptfile = filename;
|
current_script_file = filename;
|
||||||
|
|
||||||
const auto file_id = atoi(filename);
|
const auto file_id = atoi(filename);
|
||||||
if (file_id)
|
if (file_id)
|
||||||
@ -182,9 +172,9 @@ namespace scripting
|
|||||||
filename = scripting::get_token(current_file_id);
|
filename = scripting::get_token(current_file_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (script_function_table_sort.find(filename) == script_function_table_sort.end())
|
if (!script_function_table_sort.contains(filename))
|
||||||
{
|
{
|
||||||
const auto script = gsc::find_script(game::ASSET_TYPE_SCRIPTFILE, current_scriptfile.data(), false);
|
const auto script = gsc::find_script(game::ASSET_TYPE_SCRIPTFILE, current_script_file.data(), false);
|
||||||
if (script)
|
if (script)
|
||||||
{
|
{
|
||||||
const auto end = &script->bytecode[script->bytecodeLen];
|
const auto end = &script->bytecode[script->bytecodeLen];
|
||||||
@ -201,6 +191,7 @@ namespace scripting
|
|||||||
{
|
{
|
||||||
const auto name = get_token(id);
|
const auto name = get_token(id);
|
||||||
script_function_table[file][name] = pos;
|
script_function_table[file][name] = pos;
|
||||||
|
script_function_table_rev[pos] = {file, name};
|
||||||
}
|
}
|
||||||
|
|
||||||
void scr_set_thread_position_stub(unsigned int thread_name, const char* code_pos)
|
void scr_set_thread_position_stub(unsigned int thread_name, const char* code_pos)
|
||||||
@ -275,7 +266,7 @@ namespace scripting
|
|||||||
|
|
||||||
g_shutdown_game_hook.create(SELECT_VALUE(0x2A5130_b, 0x422F30_b), g_shutdown_game_stub);
|
g_shutdown_game_hook.create(SELECT_VALUE(0x2A5130_b, 0x422F30_b), g_shutdown_game_stub);
|
||||||
|
|
||||||
scheduler::loop([]()
|
scheduler::loop([]
|
||||||
{
|
{
|
||||||
lua::engine::run_frame();
|
lua::engine::run_frame();
|
||||||
}, scheduler::pipeline::server);
|
}, scheduler::pipeline::server);
|
||||||
|
@ -8,6 +8,8 @@ namespace scripting
|
|||||||
extern std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
|
extern std::unordered_map<int, std::unordered_map<std::string, int>> fields_table;
|
||||||
extern std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
|
extern std::unordered_map<std::string, std::unordered_map<std::string, const char*>> script_function_table;
|
||||||
extern std::unordered_map<std::string, std::vector<std::pair<std::string, const char*>>> script_function_table_sort;
|
extern std::unordered_map<std::string, std::vector<std::pair<std::string, const char*>>> script_function_table_sort;
|
||||||
|
extern std::unordered_map<const char*, std::pair<std::string, std::string>> script_function_table_rev;
|
||||||
|
|
||||||
extern utils::concurrency::container<shared_table_t> shared_table;
|
extern utils::concurrency::container<shared_table_t> shared_table;
|
||||||
|
|
||||||
extern std::string current_file;
|
extern std::string current_file;
|
||||||
|
@ -48,16 +48,10 @@ namespace ui_scripting
|
|||||||
const auto lui_updater = utils::nt::load_resource(LUI_UPDATER);
|
const auto lui_updater = utils::nt::load_resource(LUI_UPDATER);
|
||||||
const auto lua_json = utils::nt::load_resource(LUA_JSON);
|
const auto lua_json = utils::nt::load_resource(LUA_JSON);
|
||||||
|
|
||||||
struct script
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
std::string root;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct globals_t
|
struct globals_t
|
||||||
{
|
{
|
||||||
std::string in_require_script;
|
std::string in_require_script;
|
||||||
std::vector<script> loaded_scripts;
|
std::unordered_map<std::string, std::string> loaded_scripts;
|
||||||
bool load_raw_script{};
|
bool load_raw_script{};
|
||||||
std::string raw_script_name{};
|
std::string raw_script_name{};
|
||||||
};
|
};
|
||||||
@ -66,28 +60,13 @@ namespace ui_scripting
|
|||||||
|
|
||||||
bool is_loaded_script(const std::string& name)
|
bool is_loaded_script(const std::string& name)
|
||||||
{
|
{
|
||||||
for (auto i = globals.loaded_scripts.begin(); i != globals.loaded_scripts.end(); ++i)
|
return globals.loaded_scripts.contains(name);
|
||||||
{
|
|
||||||
if (i->name == name)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_root_script(const std::string& name)
|
std::string get_root_script(const std::string& name)
|
||||||
{
|
{
|
||||||
for (auto i = globals.loaded_scripts.begin(); i != globals.loaded_scripts.end(); ++i)
|
const auto itr = globals.loaded_scripts.find(name);
|
||||||
{
|
return itr == globals.loaded_scripts.end() ? std::string() : itr->second;
|
||||||
if (i->name == name)
|
|
||||||
{
|
|
||||||
return i->root;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table get_globals()
|
table get_globals()
|
||||||
@ -133,7 +112,7 @@ namespace ui_scripting
|
|||||||
|
|
||||||
void load_script(const std::string& name, const std::string& data)
|
void load_script(const std::string& name, const std::string& data)
|
||||||
{
|
{
|
||||||
globals.loaded_scripts.push_back({name, name});
|
globals.loaded_scripts[name] = name;
|
||||||
|
|
||||||
const auto lua = get_globals();
|
const auto lua = get_globals();
|
||||||
const auto load_results = lua["loadstring"](data, name);
|
const auto load_results = lua["loadstring"](data, name);
|
||||||
@ -451,8 +430,10 @@ namespace ui_scripting
|
|||||||
return hks_package_require_hook.invoke<void*>(state);
|
return hks_package_require_hook.invoke<void*>(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
game::XAssetHeader db_find_xasset_header_stub(game::XAssetType type, const char* name, int allow_create_default)
|
game::XAssetHeader db_find_x_asset_header_stub(game::XAssetType type, const char* name, int allow_create_default)
|
||||||
{
|
{
|
||||||
|
game::XAssetHeader header{.luaFile = nullptr};
|
||||||
|
|
||||||
if (!is_loaded_script(globals.in_require_script))
|
if (!is_loaded_script(globals.in_require_script))
|
||||||
{
|
{
|
||||||
return game::DB_FindXAssetHeader(type, name, allow_create_default);
|
return game::DB_FindXAssetHeader(type, name, allow_create_default);
|
||||||
@ -466,14 +447,14 @@ namespace ui_scripting
|
|||||||
{
|
{
|
||||||
globals.load_raw_script = true;
|
globals.load_raw_script = true;
|
||||||
globals.raw_script_name = target_script;
|
globals.raw_script_name = target_script;
|
||||||
return static_cast<game::XAssetHeader>(reinterpret_cast<game::LuaFile*>(1));
|
header.luaFile = reinterpret_cast<game::LuaFile*>(1);
|
||||||
}
|
}
|
||||||
else if (name_.starts_with("ui/LUI/"))
|
else if (name_.starts_with("ui/LUI/"))
|
||||||
{
|
{
|
||||||
return game::DB_FindXAssetHeader(type, name, allow_create_default);
|
return game::DB_FindXAssetHeader(type, name, allow_create_default);
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_cast<game::XAssetHeader>(nullptr);
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hks_load_stub(game::hks::lua_State* state, void* compiler_options,
|
int hks_load_stub(game::hks::lua_State* state, void* compiler_options,
|
||||||
@ -482,14 +463,12 @@ namespace ui_scripting
|
|||||||
if (globals.load_raw_script)
|
if (globals.load_raw_script)
|
||||||
{
|
{
|
||||||
globals.load_raw_script = false;
|
globals.load_raw_script = false;
|
||||||
globals.loaded_scripts.push_back({globals.raw_script_name, globals.in_require_script});
|
globals.loaded_scripts[globals.raw_script_name] = globals.in_require_script;
|
||||||
return load_buffer(globals.raw_script_name, utils::io::read_file(globals.raw_script_name));
|
return load_buffer(globals.raw_script_name, utils::io::read_file(globals.raw_script_name));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
return hks_load_hook.invoke<int>(state, compiler_options, reader,
|
||||||
return hks_load_hook.invoke<int>(state, compiler_options, reader,
|
reader_data, chunk_name);
|
||||||
reader_data, chunk_name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string current_error;
|
std::string current_error;
|
||||||
@ -506,7 +485,7 @@ namespace ui_scripting
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto closure = value.v.cClosure;
|
const auto closure = value.v.cClosure;
|
||||||
if (converted_functions.find(closure) == converted_functions.end())
|
if (!converted_functions.contains(closure))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -563,8 +542,8 @@ namespace ui_scripting
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::hook::call(SELECT_VALUE(0xE7419_b, 0x25E809_b), db_find_xasset_header_stub);
|
utils::hook::call(SELECT_VALUE(0xE7419_b, 0x25E809_b), db_find_x_asset_header_stub);
|
||||||
utils::hook::call(SELECT_VALUE(0xE72CB_b, 0x25E6BB_b), db_find_xasset_header_stub);
|
utils::hook::call(SELECT_VALUE(0xE72CB_b, 0x25E6BB_b), db_find_x_asset_header_stub);
|
||||||
|
|
||||||
hks_load_hook.create(SELECT_VALUE(0xB46F0_b, 0x22C180_b), hks_load_stub);
|
hks_load_hook.create(SELECT_VALUE(0xB46F0_b, 0x22C180_b), hks_load_stub);
|
||||||
|
|
||||||
@ -572,7 +551,7 @@ namespace ui_scripting
|
|||||||
hks_start_hook.create(SELECT_VALUE(0x103C50_b, 0x27A790_b), hks_start_stub);
|
hks_start_hook.create(SELECT_VALUE(0x103C50_b, 0x27A790_b), hks_start_stub);
|
||||||
hks_shutdown_hook.create(SELECT_VALUE(0xFB370_b, 0x2707C0_b), hks_shutdown_stub);
|
hks_shutdown_hook.create(SELECT_VALUE(0xFB370_b, 0x2707C0_b), hks_shutdown_stub);
|
||||||
|
|
||||||
command::add("lui_restart", []()
|
command::add("lui_restart", []
|
||||||
{
|
{
|
||||||
utils::hook::invoke<void>(SELECT_VALUE(0x1052C0_b, 0x27BEC0_b));
|
utils::hook::invoke<void>(SELECT_VALUE(0x1052C0_b, 0x27BEC0_b));
|
||||||
});
|
});
|
||||||
|
@ -116,7 +116,7 @@ namespace scripting
|
|||||||
game::AddRefToObject(id);
|
game::AddRefToObject(id);
|
||||||
|
|
||||||
const auto local_id = game::AllocThread(id);
|
const auto local_id = game::AllocThread(id);
|
||||||
const auto result = game::VM_Execute(local_id, pos, (unsigned int)arguments.size());
|
const auto result = game::VM_Execute(local_id, pos, static_cast<std::uint32_t>(arguments.size()));
|
||||||
game::RemoveRefToObject(result);
|
game::RemoveRefToObject(result);
|
||||||
|
|
||||||
return get_return_value();
|
return get_return_value();
|
||||||
@ -124,13 +124,13 @@ namespace scripting
|
|||||||
|
|
||||||
const char* get_function_pos(const std::string& filename, const std::string& function)
|
const char* get_function_pos(const std::string& filename, const std::string& function)
|
||||||
{
|
{
|
||||||
if (scripting::script_function_table.find(filename) == scripting::script_function_table.end())
|
if (!script_function_table.contains(filename))
|
||||||
{
|
{
|
||||||
throw std::runtime_error("File '" + filename + "' not found");
|
throw std::runtime_error("File '" + filename + "' not found");
|
||||||
};
|
}
|
||||||
|
|
||||||
const auto functions = scripting::script_function_table[filename];
|
const auto& functions = script_function_table[filename];
|
||||||
if (functions.find(function) == functions.end())
|
if (!functions.contains(function))
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Function '" + function + "' in file '" + filename + "' not found");
|
throw std::runtime_error("Function '" + function + "' in file '" + filename + "' not found");
|
||||||
}
|
}
|
||||||
@ -184,7 +184,7 @@ namespace scripting
|
|||||||
throw std::runtime_error("Failed to get value for field '" + field + "'");
|
throw std::runtime_error("Failed to get value for field '" + field + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto __ = gsl::finally([value]()
|
const auto _0 = gsl::finally([value]
|
||||||
{
|
{
|
||||||
game::RemoveRefToValue(value.type, value.u);
|
game::RemoveRefToValue(value.type, value.u);
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
|
|
||||||
#include "entity.hpp"
|
#include "entity.hpp"
|
||||||
#include "array.hpp"
|
#include "array.hpp"
|
||||||
|
#include "function.hpp"
|
||||||
|
|
||||||
#include "script_value.hpp"
|
#include "script_value.hpp"
|
||||||
|
|
||||||
namespace scripting
|
namespace scripting
|
||||||
|
44
src/client/game/scripting/function.cpp
Normal file
44
src/client/game/scripting/function.cpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
|
||||||
|
#include "game/scripting/function.hpp"
|
||||||
|
#include "game/scripting/execution.hpp"
|
||||||
|
|
||||||
|
#include "component/scripting.hpp"
|
||||||
|
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
function::function(const char* pos)
|
||||||
|
: pos_(pos)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value function::get_raw() const
|
||||||
|
{
|
||||||
|
game::VariableValue value;
|
||||||
|
value.type = game::SCRIPT_FUNCTION;
|
||||||
|
value.u.codePosValue = this->pos_;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* function::get_pos() const
|
||||||
|
{
|
||||||
|
return this->pos_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string function::get_name() const
|
||||||
|
{
|
||||||
|
if (scripting::script_function_table_rev.contains(this->pos_))
|
||||||
|
{
|
||||||
|
const auto& func = scripting::script_function_table_rev[this->pos_];
|
||||||
|
return utils::string::va("%s::%s", func.first.data(), func.second.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
return "unknown function";
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value function::call(const entity& self, std::vector<script_value> arguments) const
|
||||||
|
{
|
||||||
|
return exec_ent_thread(self, this->pos_, arguments);
|
||||||
|
}
|
||||||
|
}
|
35
src/client/game/scripting/function.hpp
Normal file
35
src/client/game/scripting/function.hpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "entity.hpp"
|
||||||
|
#include "script_value.hpp"
|
||||||
|
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
class function
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
function(const char*);
|
||||||
|
|
||||||
|
script_value get_raw() const;
|
||||||
|
const char* get_pos() const;
|
||||||
|
std::string get_name() const;
|
||||||
|
|
||||||
|
script_value call(const entity& self, std::vector<script_value> arguments) const;
|
||||||
|
|
||||||
|
script_value operator()(const entity& self, std::vector<script_value> arguments) const
|
||||||
|
{
|
||||||
|
return this->call(self, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value operator()(std::vector<script_value> arguments) const
|
||||||
|
{
|
||||||
|
return this->call(*game::levelEntityId, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value operator()() const
|
||||||
|
{
|
||||||
|
return this->call(*game::levelEntityId, {});
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
const char* pos_;
|
||||||
|
};
|
||||||
|
}
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include <xsk/gsc/types.hpp>
|
#include <xsk/gsc/types.hpp>
|
||||||
#include <xsk/resolver.hpp>
|
#include <xsk/resolver.hpp>
|
||||||
#include <xsk/utils/compression.hpp>
|
|
||||||
|
|
||||||
#include <utils/string.hpp>
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
|
@ -3,18 +3,16 @@
|
|||||||
#include "error.hpp"
|
#include "error.hpp"
|
||||||
#include "value_conversion.hpp"
|
#include "value_conversion.hpp"
|
||||||
|
|
||||||
#include "../execution.hpp"
|
#include "game/scripting/execution.hpp"
|
||||||
#include "../functions.hpp"
|
|
||||||
|
|
||||||
#include "../../../component/command.hpp"
|
#include "component/command.hpp"
|
||||||
#include "../../../component/logfile.hpp"
|
#include "component/logfile.hpp"
|
||||||
#include "../../../component/scripting.hpp"
|
#include "component/scripting.hpp"
|
||||||
#include "../../../component/fastfiles.hpp"
|
#include "component/fastfiles.hpp"
|
||||||
#include "../../../component/scheduler.hpp"
|
#include "component/scheduler.hpp"
|
||||||
|
|
||||||
#include <xsk/gsc/types.hpp>
|
#include <xsk/gsc/types.hpp>
|
||||||
#include <xsk/resolver.hpp>
|
#include <xsk/resolver.hpp>
|
||||||
#include <xsk/utils/compression.hpp>
|
|
||||||
|
|
||||||
#include <utils/string.hpp>
|
#include <utils/string.hpp>
|
||||||
#include <utils/io.hpp>
|
#include <utils/io.hpp>
|
||||||
@ -221,9 +219,8 @@ namespace scripting::lua
|
|||||||
|
|
||||||
for (const auto& func : xsk::gsc::h1::resolver::get_methods())
|
for (const auto& func : xsk::gsc::h1::resolver::get_methods())
|
||||||
{
|
{
|
||||||
const auto func_name = std::string(func.first);
|
const auto name = std::string(func.first);
|
||||||
const auto name = utils::string::to_lower(func_name);
|
entity_type[name] = [name](const entity& entity, const sol::this_state s, sol::variadic_args va)
|
||||||
entity_type[name.data()] = [name](const entity& entity, const sol::this_state s, sol::variadic_args va)
|
|
||||||
{
|
{
|
||||||
std::vector<script_value> arguments{};
|
std::vector<script_value> arguments{};
|
||||||
|
|
||||||
@ -311,13 +308,13 @@ namespace scripting::lua
|
|||||||
entity_type["getstruct"] = [](const entity& entity, const sol::this_state s)
|
entity_type["getstruct"] = [](const entity& entity, const sol::this_state s)
|
||||||
{
|
{
|
||||||
const auto id = entity.get_entity_id();
|
const auto id = entity.get_entity_id();
|
||||||
return scripting::lua::entity_to_struct(s, id);
|
return entity_to_struct(s, id);
|
||||||
};
|
};
|
||||||
|
|
||||||
entity_type["struct"] = sol::property([](const entity& entity, const sol::this_state s)
|
entity_type["struct"] = sol::property([](const entity& entity, const sol::this_state s)
|
||||||
{
|
{
|
||||||
const auto id = entity.get_entity_id();
|
const auto id = entity.get_entity_id();
|
||||||
return scripting::lua::entity_to_struct(s, id);
|
return entity_to_struct(s, id);
|
||||||
});
|
});
|
||||||
|
|
||||||
entity_type["scriptcall"] = [](const entity& entity, const sol::this_state s, const std::string& filename,
|
entity_type["scriptcall"] = [](const entity& entity, const sol::this_state s, const std::string& filename,
|
||||||
@ -341,8 +338,7 @@ namespace scripting::lua
|
|||||||
|
|
||||||
for (const auto& func : xsk::gsc::h1::resolver::get_functions())
|
for (const auto& func : xsk::gsc::h1::resolver::get_functions())
|
||||||
{
|
{
|
||||||
const auto func_name = std::string(func.first);
|
const auto name = std::string(func.first);
|
||||||
const auto name = utils::string::to_lower(func_name);
|
|
||||||
game_type[name] = [name](const game&, const sol::this_state s, sol::variadic_args va)
|
game_type[name] = [name](const game&, const sol::this_state s, sol::variadic_args va)
|
||||||
{
|
{
|
||||||
std::vector<script_value> arguments{};
|
std::vector<script_value> arguments{};
|
||||||
@ -409,7 +405,7 @@ namespace scripting::lua
|
|||||||
|
|
||||||
game_type["getfunctions"] = [entity_type](const game&, const sol::this_state s, const std::string& filename)
|
game_type["getfunctions"] = [entity_type](const game&, const sol::this_state s, const std::string& filename)
|
||||||
{
|
{
|
||||||
if (scripting::script_function_table.find(filename) == scripting::script_function_table.end())
|
if (!script_function_table.contains(filename))
|
||||||
{
|
{
|
||||||
throw std::runtime_error("File '" + filename + "' not found");
|
throw std::runtime_error("File '" + filename + "' not found");
|
||||||
}
|
}
|
||||||
@ -428,7 +424,7 @@ namespace scripting::lua
|
|||||||
arguments.push_back(convert({s, arg}));
|
arguments.push_back(convert({s, arg}));
|
||||||
}
|
}
|
||||||
|
|
||||||
gsl::finally(&logfile::enable_vm_execute_hook);
|
const auto _0 = gsl::finally(&logfile::enable_vm_execute_hook);
|
||||||
logfile::disable_vm_execute_hook();
|
logfile::disable_vm_execute_hook();
|
||||||
|
|
||||||
return convert(s, call_script_function(entity, filename, function.first, arguments));
|
return convert(s, call_script_function(entity, filename, function.first, arguments));
|
||||||
@ -442,7 +438,7 @@ namespace scripting::lua
|
|||||||
arguments.push_back(convert({s, arg}));
|
arguments.push_back(convert({s, arg}));
|
||||||
}
|
}
|
||||||
|
|
||||||
gsl::finally(&logfile::enable_vm_execute_hook);
|
const auto _0 = gsl::finally(&logfile::enable_vm_execute_hook);
|
||||||
logfile::disable_vm_execute_hook();
|
logfile::disable_vm_execute_hook();
|
||||||
|
|
||||||
return convert(s, call_script_function(*::game::levelEntityId, filename, function.first, arguments));
|
return convert(s, call_script_function(*::game::levelEntityId, filename, function.first, arguments));
|
||||||
@ -463,7 +459,7 @@ namespace scripting::lua
|
|||||||
arguments.push_back(convert({s, arg}));
|
arguments.push_back(convert({s, arg}));
|
||||||
}
|
}
|
||||||
|
|
||||||
gsl::finally(&logfile::enable_vm_execute_hook);
|
const auto _0 = gsl::finally(&logfile::enable_vm_execute_hook);
|
||||||
logfile::disable_vm_execute_hook();
|
logfile::disable_vm_execute_hook();
|
||||||
|
|
||||||
return convert(s, call_script_function(*::game::levelEntityId, filename, function, arguments));
|
return convert(s, call_script_function(*::game::levelEntityId, filename, function, arguments));
|
||||||
@ -497,7 +493,7 @@ namespace scripting::lua
|
|||||||
arguments.push_back(convert({s, arg}));
|
arguments.push_back(convert({s, arg}));
|
||||||
}
|
}
|
||||||
|
|
||||||
gsl::finally(&logfile::enable_vm_execute_hook);
|
const auto _0 = gsl::finally(&logfile::enable_vm_execute_hook);
|
||||||
logfile::disable_vm_execute_hook();
|
logfile::disable_vm_execute_hook();
|
||||||
|
|
||||||
return convert(s, call_script_function(entity, filename, function_name, arguments));
|
return convert(s, call_script_function(entity, filename, function_name, arguments));
|
||||||
@ -511,7 +507,7 @@ namespace scripting::lua
|
|||||||
arguments.push_back(convert({s, arg}));
|
arguments.push_back(convert({s, arg}));
|
||||||
}
|
}
|
||||||
|
|
||||||
gsl::finally(&logfile::enable_vm_execute_hook);
|
const auto _0 = gsl::finally(&logfile::enable_vm_execute_hook);
|
||||||
logfile::disable_vm_execute_hook();
|
logfile::disable_vm_execute_hook();
|
||||||
|
|
||||||
return convert(s, call_script_function(*::game::levelEntityId, filename, function_name, arguments));
|
return convert(s, call_script_function(*::game::levelEntityId, filename, function_name, arguments));
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "value_conversion.hpp"
|
#include "value_conversion.hpp"
|
||||||
#include "../functions.hpp"
|
#include "game/scripting/functions.hpp"
|
||||||
#include "../execution.hpp"
|
#include "game/scripting/execution.hpp"
|
||||||
#include ".../../component/logfile.hpp"
|
#include "component/logfile.hpp"
|
||||||
|
|
||||||
namespace scripting::lua
|
namespace scripting::lua
|
||||||
{
|
{
|
||||||
@ -275,7 +275,7 @@ namespace scripting::lua
|
|||||||
return entity_to_array(state, value.get_raw().u.uintValue);
|
return entity_to_array(state, value.get_raw().u.uintValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.is<std::function<void()>>())
|
if (value.is<function>())
|
||||||
{
|
{
|
||||||
return convert_function(state, value.get_raw().u.codePosValue);
|
return convert_function(state, value.get_raw().u.codePosValue);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include "script_value.hpp"
|
#include "script_value.hpp"
|
||||||
#include "entity.hpp"
|
#include "entity.hpp"
|
||||||
#include "array.hpp"
|
#include "array.hpp"
|
||||||
#include "functions.hpp"
|
#include "function.hpp"
|
||||||
|
|
||||||
namespace scripting
|
namespace scripting
|
||||||
{
|
{
|
||||||
@ -20,7 +20,6 @@ namespace scripting
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
script_value::script_value(const int value)
|
script_value::script_value(const int value)
|
||||||
{
|
{
|
||||||
game::VariableValue variable{};
|
game::VariableValue variable{};
|
||||||
@ -95,6 +94,15 @@ namespace scripting
|
|||||||
this->value_ = variable;
|
this->value_ = variable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const function& value)
|
||||||
|
{
|
||||||
|
game::VariableValue variable{};
|
||||||
|
variable.type = game::SCRIPT_FUNCTION;
|
||||||
|
variable.u.codePosValue = value.get_pos();
|
||||||
|
|
||||||
|
this->value_ = variable;
|
||||||
|
}
|
||||||
|
|
||||||
script_value::script_value(const vector& value)
|
script_value::script_value(const vector& value)
|
||||||
{
|
{
|
||||||
game::VariableValue variable{};
|
game::VariableValue variable{};
|
||||||
@ -252,11 +260,17 @@ namespace scripting
|
|||||||
**************************************************************/
|
**************************************************************/
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
bool script_value::is<std::function<void()>>() const
|
bool script_value::is<function>() const
|
||||||
{
|
{
|
||||||
return this->get_raw().type == game::SCRIPT_FUNCTION;
|
return this->get_raw().type == game::SCRIPT_FUNCTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
function script_value::get() const
|
||||||
|
{
|
||||||
|
return function(this->get_raw().u.codePosValue);
|
||||||
|
}
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
* Entity
|
* Entity
|
||||||
**************************************************************/
|
**************************************************************/
|
||||||
@ -331,9 +345,10 @@ namespace scripting
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->is<std::function<void()>>())
|
if (this->is<function>())
|
||||||
{
|
{
|
||||||
return utils::string::va("[[ function ]]");
|
const auto func = this->as<function>();
|
||||||
|
return utils::string::va("[[ %s ]]", func.get_name().data());
|
||||||
}
|
}
|
||||||
|
|
||||||
return this->type_name();
|
return this->type_name();
|
||||||
|
@ -9,8 +9,95 @@ namespace scripting
|
|||||||
{
|
{
|
||||||
class entity;
|
class entity;
|
||||||
class array;
|
class array;
|
||||||
|
class function;
|
||||||
class value_wrap;
|
class value_wrap;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::unordered_map<int, std::string> typenames =
|
||||||
|
{
|
||||||
|
{0, "undefined"},
|
||||||
|
{1, "object"},
|
||||||
|
{2, "string"},
|
||||||
|
{3, "localized string"},
|
||||||
|
{4, "vector"},
|
||||||
|
{5, "float"},
|
||||||
|
{6, "int"},
|
||||||
|
{7, "codepos"},
|
||||||
|
{8, "precodepos"},
|
||||||
|
{9, "function"},
|
||||||
|
{10, "builtin function"},
|
||||||
|
{11, "builtin method"},
|
||||||
|
{12, "stack"},
|
||||||
|
{13, "animation"},
|
||||||
|
{14, "developer codepos"}, // this exists on H1 but not IW6
|
||||||
|
{15, "pre animation"},
|
||||||
|
{16, "thread"},
|
||||||
|
{17, "notify thread"},
|
||||||
|
{18, "time thread"},
|
||||||
|
{19, "child thread"},
|
||||||
|
{20, "struct"},
|
||||||
|
{21, "removed entity"},
|
||||||
|
{22, "entity"},
|
||||||
|
{23, "array"},
|
||||||
|
{24, "removed thread"},
|
||||||
|
{25, "<free>"}, // VAR_COUNT is 25 on H1, but 24 on IW6
|
||||||
|
{26, "thread list"},
|
||||||
|
{27, "endon list"},
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string get_typename(const game::VariableValue& value)
|
||||||
|
{
|
||||||
|
if (value.type == game::SCRIPT_OBJECT)
|
||||||
|
{
|
||||||
|
const auto type = game::scr_VarGlob->objectVariableValue[value.u.uintValue].w.type;
|
||||||
|
return typenames[type];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return typenames[value.type];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename A = array>
|
||||||
|
std::string get_c_typename()
|
||||||
|
{
|
||||||
|
auto& info = typeid(T);
|
||||||
|
|
||||||
|
if (info == typeid(std::string))
|
||||||
|
{
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info == typeid(const char*))
|
||||||
|
{
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info == typeid(entity))
|
||||||
|
{
|
||||||
|
return "entity";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info == typeid(array))
|
||||||
|
{
|
||||||
|
return "array";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info == typeid(function))
|
||||||
|
{
|
||||||
|
return "function";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info == typeid(vector))
|
||||||
|
{
|
||||||
|
return "vector";
|
||||||
|
}
|
||||||
|
|
||||||
|
return info.name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class script_value
|
class script_value
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -31,25 +118,28 @@ namespace scripting
|
|||||||
script_value(const entity& value);
|
script_value(const entity& value);
|
||||||
script_value(const array& value);
|
script_value(const array& value);
|
||||||
|
|
||||||
|
script_value(const function& value);
|
||||||
|
|
||||||
script_value(const vector& value);
|
script_value(const vector& value);
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
bool is() const;
|
|
||||||
|
|
||||||
// was gonna do this but no clue if this is the same on H1 so just return string (https://github.com/fedddddd/t6-gsc-utils/blob/main/src/game/scripting/script_value.hpp#L18)
|
|
||||||
std::string type_name() const
|
std::string type_name() const
|
||||||
{
|
{
|
||||||
return utils::string::va("%s", this->get_raw().type);
|
return get_typename(this->get_raw());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string to_string() const;
|
std::string to_string() const;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool is() const;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T as() const
|
T as() const
|
||||||
{
|
{
|
||||||
if (!this->is<T>())
|
if (!this->is<T>())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Invalid type");
|
const auto type = get_typename(this->get_raw());
|
||||||
|
const auto c_type = get_c_typename<T>();
|
||||||
|
throw std::runtime_error(std::format("has type '{}' but should be '{}'", type, c_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
return get<T>();
|
return get<T>();
|
||||||
@ -58,7 +148,6 @@ namespace scripting
|
|||||||
template <typename T, typename I = int>
|
template <typename T, typename I = int>
|
||||||
T* as_ptr()
|
T* as_ptr()
|
||||||
{
|
{
|
||||||
|
|
||||||
const auto value = this->as<I>();
|
const auto value = this->as<I>();
|
||||||
|
|
||||||
if (!value)
|
if (!value)
|
||||||
@ -72,11 +161,9 @@ namespace scripting
|
|||||||
const game::VariableValue& get_raw() const;
|
const game::VariableValue& get_raw() const;
|
||||||
|
|
||||||
variable_value value_{};
|
variable_value value_{};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T get() const;
|
T get() const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class value_wrap
|
class value_wrap
|
||||||
@ -84,6 +171,16 @@ namespace scripting
|
|||||||
public:
|
public:
|
||||||
value_wrap(const scripting::script_value& value, int argument_index);
|
value_wrap(const scripting::script_value& value, int argument_index);
|
||||||
|
|
||||||
|
std::string to_string() const
|
||||||
|
{
|
||||||
|
return this->value_.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string type_name() const
|
||||||
|
{
|
||||||
|
return this->value_.type_name();
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T as() const
|
T as() const
|
||||||
{
|
{
|
||||||
|
@ -37,6 +37,43 @@ namespace game
|
|||||||
SCRIPT_ARRAY = 22
|
SCRIPT_ARRAY = 22
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// recreated from IW6 pdb and H1 elf (no structs :/)
|
||||||
|
enum VariableType
|
||||||
|
{
|
||||||
|
VAR_UNDEFINED = 0x0,
|
||||||
|
VAR_BEGIN_REF = 0x1,
|
||||||
|
VAR_POINTER = 0x1,
|
||||||
|
VAR_STRING = 0x2,
|
||||||
|
VAR_ISTRING = 0x3,
|
||||||
|
VAR_VECTOR = 0x4,
|
||||||
|
VAR_END_REF = 0x5,
|
||||||
|
VAR_FLOAT = 0x5,
|
||||||
|
VAR_INTEGER = 0x6,
|
||||||
|
VAR_CODEPOS = 0x7,
|
||||||
|
VAR_PRECODEPOS = 0x8,
|
||||||
|
VAR_FUNCTION = 0x9,
|
||||||
|
VAR_BUILTIN_FUNCTION = 0xA,
|
||||||
|
VAR_BUILTIN_METHOD = 0xB,
|
||||||
|
VAR_STACK = 0xC,
|
||||||
|
VAR_ANIMATION = 0xD,
|
||||||
|
VAR_DEVELOPER_CODEPOS = 0xE,
|
||||||
|
VAR_PRE_ANIMATION = 0xF,
|
||||||
|
VAR_THREAD = 0x10,
|
||||||
|
VAR_NOTIFY_THREAD = 0x11,
|
||||||
|
VAR_TIME_THREAD = 0x12,
|
||||||
|
VAR_CHILD_THREAD = 0x13,
|
||||||
|
VAR_OBJECT = 0x14,
|
||||||
|
VAR_DEAD_ENTITY = 0x15,
|
||||||
|
VAR_ENTITY = 0x16,
|
||||||
|
VAR_ARRAY = 0x17,
|
||||||
|
VAR_DEAD_THREAD = 0x18,
|
||||||
|
VAR_COUNT = 0x19,
|
||||||
|
VAR_FREE = 0x19,
|
||||||
|
VAR_THREAD_LIST = 0x1A,
|
||||||
|
VAR_ENDON_LIST = 0x1B,
|
||||||
|
VAR_TOTAL_COUNT = 0x1C,
|
||||||
|
};
|
||||||
|
|
||||||
struct VariableStackBuffer
|
struct VariableStackBuffer
|
||||||
{
|
{
|
||||||
const char* pos;
|
const char* pos;
|
||||||
|
@ -346,6 +346,6 @@ namespace game
|
|||||||
WEAK symbol<int(lua_State* s, int level, lua_Debug* ar)> hksi_lua_getstack{0xB87A0, 0x2302B0};
|
WEAK symbol<int(lua_State* s, int level, lua_Debug* ar)> hksi_lua_getstack{0xB87A0, 0x2302B0};
|
||||||
WEAK symbol<void(lua_State* s, const char* fmt, ...)> hksi_luaL_error{0xBF120, 0x22F930};
|
WEAK symbol<void(lua_State* s, const char* fmt, ...)> hksi_luaL_error{0xBF120, 0x22F930};
|
||||||
WEAK symbol<void(lua_State* s, int what, int data)> hksi_lua_gc{0, 0x236EF0};
|
WEAK symbol<void(lua_State* s, int what, int data)> hksi_lua_gc{0, 0x236EF0};
|
||||||
WEAK symbol<const char*> typenames{0x98CD20, 0x10AD750};
|
WEAK symbol<const char*> s_compilerTypeName{0x98CD20, 0x10AD750};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ namespace ui_scripting
|
|||||||
values.push_back(v);
|
values.push_back(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.size() == 0)
|
if (values.empty())
|
||||||
{
|
{
|
||||||
values.push_back({});
|
values.push_back({});
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ namespace ui_scripting
|
|||||||
values.push_back(v);
|
values.push_back(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.size() == 0)
|
if (values.empty())
|
||||||
{
|
{
|
||||||
values.push_back({});
|
values.push_back({});
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ namespace ui_scripting
|
|||||||
{
|
{
|
||||||
if (!this->is<T>())
|
if (!this->is<T>())
|
||||||
{
|
{
|
||||||
const auto hks_typename = game::hks::typenames[this->get_raw().t + 2];
|
const auto hks_typename = game::hks::s_compilerTypeName[this->get_raw().t + 2];
|
||||||
const auto typename_ = get_typename<T>();
|
const auto typename_ = get_typename<T>();
|
||||||
|
|
||||||
throw std::runtime_error(utils::string::va("%s expected, got %s",
|
throw std::runtime_error(utils::string::va("%s expected, got %s",
|
||||||
|
@ -88,6 +88,7 @@
|
|||||||
#include <udis86.h>
|
#include <udis86.h>
|
||||||
#include <MinHook.h>
|
#include <MinHook.h>
|
||||||
#include <tomcrypt.h>
|
#include <tomcrypt.h>
|
||||||
|
#include <json.hpp>
|
||||||
|
|
||||||
#define RAPIDJSON_NOEXCEPT
|
#define RAPIDJSON_NOEXCEPT
|
||||||
#define RAPIDJSON_ASSERT(cond) if(cond); else throw std::runtime_error("rapidjson assert fail");
|
#define RAPIDJSON_ASSERT(cond) if(cond); else throw std::runtime_error("rapidjson assert fail");
|
||||||
|
Loading…
Reference in New Issue
Block a user