mirror of
https://github.com/alicealys/t5-gsc-utils.git
synced 2025-08-30 05:13:17 +00:00
add mysql + update build
This commit is contained in:
38
.github/workflows/build.yml
vendored
38
.github/workflows/build.yml
vendored
@@ -8,6 +8,9 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- "*"
|
- "*"
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.ref }}
|
||||||
|
cancel-in-progress: false
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build binaries
|
name: Build binaries
|
||||||
@@ -23,11 +26,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
# NOTE - If LFS ever starts getting used during builds, switch this to true!
|
|
||||||
lfs: false
|
lfs: false
|
||||||
|
|
||||||
- name: Add msbuild to PATH
|
- name: Add msbuild to PATH
|
||||||
uses: microsoft/setup-msbuild@v1.3.1
|
uses: microsoft/setup-msbuild@v2
|
||||||
|
|
||||||
- name: Generate project files
|
- name: Generate project files
|
||||||
run: tools/premake5 vs2022
|
run: tools/premake5 vs2022
|
||||||
@@ -44,3 +46,35 @@ jobs:
|
|||||||
name: ${{matrix.configuration}} binaries
|
name: ${{matrix.configuration}} binaries
|
||||||
path: |
|
path: |
|
||||||
build/bin/${{matrix.configuration}}/t5-gsc-utils.dll
|
build/bin/${{matrix.configuration}}/t5-gsc-utils.dll
|
||||||
|
deploy:
|
||||||
|
name: Deploy artifacts
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')
|
||||||
|
steps:
|
||||||
|
- name: Setup main environment
|
||||||
|
if: github.ref == 'refs/heads/main'
|
||||||
|
run: echo "ALICE_MASTER_PATH=${{ secrets.ALICE_MASTER_SSH_PATH }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Download Release binaries
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Release binaries
|
||||||
|
|
||||||
|
- name: Install SSH key
|
||||||
|
uses: shimataro/ssh-key-action@v2.7.0
|
||||||
|
with:
|
||||||
|
key: ${{ secrets.ALICE_MASTER_SSH_PRIVATE_KEY }}
|
||||||
|
known_hosts: 'just-a-placeholder-so-we-dont-get-errors'
|
||||||
|
|
||||||
|
- name: Add known hosts
|
||||||
|
run: ssh-keyscan -H ${{ secrets.ALICE_MASTER_SSH_ADDRESS }} >> ~/.ssh/known_hosts
|
||||||
|
|
||||||
|
- name: Remove old data files
|
||||||
|
run: ssh ${{ secrets.ALICE_MASTER_SSH_USER }}@${{ secrets.ALICE_MASTER_SSH_ADDRESS }} rm -rf ${{ env.ALICE_MASTER_PATH }}/t5-gsc-utils/*
|
||||||
|
|
||||||
|
- name: Upload binary
|
||||||
|
run: rsync -avz t5-gsc-utils.dll ${{ secrets.ALICE_MASTER_SSH_USER }}@${{ secrets.ALICE_MASTER_SSH_ADDRESS }}:${{ env.ALICE_MASTER_PATH }}/t5-gsc-utils/
|
||||||
|
|
||||||
|
- name: Publish changes
|
||||||
|
run: ssh ${{ secrets.ALICE_MASTER_SSH_USER }}@${{ secrets.ALICE_MASTER_SSH_ADDRESS }} ${{ secrets.ALICE_MASTER_SSH_CHANGE_PUBLISH_COMMAND }}
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -148,3 +148,5 @@ UpgradeLog*.htm
|
|||||||
# User scripts
|
# User scripts
|
||||||
user*.bat
|
user*.bat
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
|
|
||||||
|
deps/mysql
|
||||||
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -31,3 +31,9 @@
|
|||||||
[submodule "deps/plutonium-sdk"]
|
[submodule "deps/plutonium-sdk"]
|
||||||
path = deps/plutonium-sdk
|
path = deps/plutonium-sdk
|
||||||
url = https://github.com/plutoniummod/plutonium-sdk.git
|
url = https://github.com/plutoniummod/plutonium-sdk.git
|
||||||
|
[submodule "deps/sqlpp11"]
|
||||||
|
path = deps/sqlpp11
|
||||||
|
url = https://github.com/rbock/sqlpp11.git
|
||||||
|
[submodule "deps/date"]
|
||||||
|
path = deps/date
|
||||||
|
url = https://github.com/HowardHinnant/date
|
||||||
|
1
deps/date
vendored
Submodule
1
deps/date
vendored
Submodule
Submodule deps/date added at e32a0d809c
52
deps/premake/mysql.lua
vendored
Normal file
52
deps/premake/mysql.lua
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
if (os.host() ~= "windows") then
|
||||||
|
error("automatic mysql installation is not supported on your os")
|
||||||
|
end
|
||||||
|
|
||||||
|
mysql = {
|
||||||
|
source = path.join(dependencies.basePath, "mysql"),
|
||||||
|
version = "5.7.43",
|
||||||
|
download = "https://cdn.alicent.cat/mysql-5.7.43-win32.zip",
|
||||||
|
}
|
||||||
|
|
||||||
|
function mysql.install()
|
||||||
|
local hfile = io.open(string.format("%s/include/mysql.h", mysql.source), "r")
|
||||||
|
if (hfile) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
os.execute(string.format("mkdir \"%s\" 2> nul", mysql.source))
|
||||||
|
|
||||||
|
local folder = path.join(mysql.source, "mysql-5.7.43-win32")
|
||||||
|
local archive = path.join(mysql.source, "mysql-5.7.43-win32.zip")
|
||||||
|
|
||||||
|
print("Downloading MYSQL")
|
||||||
|
os.execute(string.format("curl \"%s\" -L -o \"%s\"", mysql.download, archive))
|
||||||
|
|
||||||
|
os.execute(string.format("powershell -command \"Expand-Archive -Force \\\"%s\\\" \\\"%s\\\"\"", archive, mysql.source))
|
||||||
|
os.execute(string.format("powershell -command \"mv \\\"%s/*\\\" \\\"%s\\\"\"", folder, mysql.source))
|
||||||
|
os.execute(string.format("powershell -command \"rm \\\"%s\\\"\"", archive))
|
||||||
|
os.execute(string.format("rmdir \"%s\"", folder))
|
||||||
|
end
|
||||||
|
|
||||||
|
function mysql.import()
|
||||||
|
mysql.install()
|
||||||
|
mysql.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function mysql.includes()
|
||||||
|
includedirs {
|
||||||
|
path.join(mysql.source, "include"),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function mysql.project()
|
||||||
|
project "mysql"
|
||||||
|
language "C"
|
||||||
|
|
||||||
|
mysql.includes()
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, mysql)
|
26
deps/premake/sqlpp11.lua
vendored
Normal file
26
deps/premake/sqlpp11.lua
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
sqlpp11 = {
|
||||||
|
source = path.join(dependencies.basePath, "sqlpp11"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function sqlpp11.import()
|
||||||
|
sqlpp11.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function sqlpp11.includes()
|
||||||
|
includedirs {
|
||||||
|
path.join(sqlpp11.source, "include"),
|
||||||
|
path.join(dependencies.basePath, "date/include"),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function sqlpp11.project()
|
||||||
|
project "sqlpp11"
|
||||||
|
language "C++"
|
||||||
|
|
||||||
|
sqlpp11.includes()
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, sqlpp11)
|
1
deps/sqlpp11
vendored
Submodule
1
deps/sqlpp11
vendored
Submodule
Submodule deps/sqlpp11 added at 1806fe4dd8
@@ -77,19 +77,25 @@ workspace "t5-gsc-utils"
|
|||||||
"./src/**.h",
|
"./src/**.h",
|
||||||
"./src/**.hpp",
|
"./src/**.hpp",
|
||||||
"./src/**.cpp",
|
"./src/**.cpp",
|
||||||
|
"./src/**.rc",
|
||||||
}
|
}
|
||||||
|
|
||||||
includedirs
|
includedirs
|
||||||
{
|
{
|
||||||
"%{prj.location}/src",
|
"%{prj.location}/src",
|
||||||
"./src",
|
"./src",
|
||||||
|
"./deps/mysql/include"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
libdirs {"./deps/mysql/lib"}
|
||||||
|
|
||||||
resincludedirs
|
resincludedirs
|
||||||
{
|
{
|
||||||
"$(ProjectDir)src"
|
"$(ProjectDir)src"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
linkoptions {"/DELAYLOAD:libmysql.dll"}
|
||||||
|
|
||||||
pchheader "stdinc.hpp"
|
pchheader "stdinc.hpp"
|
||||||
pchsource "src/stdinc.cpp"
|
pchsource "src/stdinc.cpp"
|
||||||
|
|
||||||
|
@@ -288,7 +288,7 @@ namespace command
|
|||||||
|
|
||||||
for (auto i = 0; i < params.size(); i++)
|
for (auto i = 0; i < params.size(); i++)
|
||||||
{
|
{
|
||||||
array.push(params[i]);
|
array.emplace_back(params[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function({array});
|
function({array});
|
||||||
@@ -309,7 +309,7 @@ namespace command
|
|||||||
|
|
||||||
for (auto i = 0; i < params.size(); i++)
|
for (auto i = 0; i < params.size(); i++)
|
||||||
{
|
{
|
||||||
array.push(params[i]);
|
array.emplace_back(params[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function(player, {array});
|
function(player, {array});
|
||||||
|
@@ -136,12 +136,12 @@ namespace exception
|
|||||||
public:
|
public:
|
||||||
void on_startup([[maybe_unused]] plugin::plugin* plugin) override
|
void on_startup([[maybe_unused]] plugin::plugin* plugin) override
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
//#ifdef DEBUG
|
||||||
SetUnhandledExceptionFilter(exception_filter);
|
SetUnhandledExceptionFilter(exception_filter);
|
||||||
utils::hook::jump(reinterpret_cast<uintptr_t>(&SetUnhandledExceptionFilter), set_unhandled_exception_filter_stub);
|
utils::hook::jump(reinterpret_cast<uintptr_t>(&SetUnhandledExceptionFilter), set_unhandled_exception_filter_stub);
|
||||||
#endif
|
//#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
//REGISTER_COMPONENT(exception::component)
|
REGISTER_COMPONENT(exception::component)
|
||||||
|
@@ -205,7 +205,7 @@ namespace gsc
|
|||||||
|
|
||||||
for (const auto& arg : va)
|
for (const auto& arg : va)
|
||||||
{
|
{
|
||||||
array.push(arg);
|
array.emplace_back(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return array;
|
return array;
|
||||||
|
@@ -173,7 +173,7 @@ namespace json
|
|||||||
|
|
||||||
for (const auto& [key, value] : obj.items())
|
for (const auto& [key, value] : obj.items())
|
||||||
{
|
{
|
||||||
array.push(json_to_gsc(value));
|
array.emplace_back(json_to_gsc(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return array.get_raw();
|
return array.get_raw();
|
||||||
|
499
src/component/mysql.cpp
Normal file
499
src/component/mysql.cpp
Normal file
@@ -0,0 +1,499 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include "mysql.hpp"
|
||||||
|
#include "component/gsc.hpp"
|
||||||
|
#include "component/scheduler.hpp"
|
||||||
|
#include "component/scripting.hpp"
|
||||||
|
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
|
namespace mysql
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct mysql_result_t
|
||||||
|
{
|
||||||
|
MYSQL_RES* result;
|
||||||
|
MYSQL_STMT* stmt;
|
||||||
|
uint64_t affected_rows;
|
||||||
|
std::string error;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct task_t
|
||||||
|
{
|
||||||
|
std::thread thread;
|
||||||
|
bool done;
|
||||||
|
bool canceled;
|
||||||
|
std::unique_ptr<scripting::object> handle;
|
||||||
|
mysql_result_t result;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint64_t task_index{};
|
||||||
|
std::unordered_map<uint64_t, task_t> tasks;
|
||||||
|
|
||||||
|
scripting::script_value field_to_value(const MYSQL_FIELD* field, const std::string& row)
|
||||||
|
{
|
||||||
|
switch (field->type)
|
||||||
|
{
|
||||||
|
case enum_field_types::MYSQL_TYPE_INT24:
|
||||||
|
case enum_field_types::MYSQL_TYPE_LONG:
|
||||||
|
case enum_field_types::MYSQL_TYPE_SHORT:
|
||||||
|
return std::atoi(row.data());
|
||||||
|
case enum_field_types::MYSQL_TYPE_LONGLONG:
|
||||||
|
return row;
|
||||||
|
case enum_field_types::MYSQL_TYPE_FLOAT:
|
||||||
|
case enum_field_types::MYSQL_TYPE_DOUBLE:
|
||||||
|
return static_cast<float>(std::atof(row.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
scripting::script_value bind_to_value(const MYSQL_BIND* bind)
|
||||||
|
{
|
||||||
|
switch (bind->buffer_type)
|
||||||
|
{
|
||||||
|
case enum_field_types::MYSQL_TYPE_INT24:
|
||||||
|
case enum_field_types::MYSQL_TYPE_LONG:
|
||||||
|
case enum_field_types::MYSQL_TYPE_SHORT:
|
||||||
|
return *reinterpret_cast<int*>(bind->buffer);
|
||||||
|
case enum_field_types::MYSQL_TYPE_LONGLONG:
|
||||||
|
return std::to_string(*reinterpret_cast<std::int64_t*>(bind->buffer));
|
||||||
|
case enum_field_types::MYSQL_TYPE_FLOAT:
|
||||||
|
return *reinterpret_cast<float*>(bind->buffer);
|
||||||
|
case enum_field_types::MYSQL_TYPE_DOUBLE:
|
||||||
|
return static_cast<float>(*reinterpret_cast<double*>(bind->buffer));
|
||||||
|
case enum_field_types::MYSQL_TYPE_STRING:
|
||||||
|
return std::string{reinterpret_cast<char*>(bind->buffer), bind->buffer_length};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
scripting::array generate_result(MYSQL_STMT* stmt)
|
||||||
|
{
|
||||||
|
if (stmt == nullptr)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::memory::allocator allocator;
|
||||||
|
|
||||||
|
scripting::array result_arr;
|
||||||
|
|
||||||
|
const auto meta = mysql_stmt_result_metadata(stmt);
|
||||||
|
if (meta == nullptr)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto column_count = mysql_num_fields(meta);
|
||||||
|
const auto fields = mysql_fetch_fields(meta);
|
||||||
|
|
||||||
|
const auto is_null = allocator.allocate_array<my_bool>(column_count);
|
||||||
|
const auto errors = allocator.allocate_array<my_bool>(column_count);
|
||||||
|
const auto real_lengths = allocator.allocate_array<unsigned long>(column_count);
|
||||||
|
const auto binds = allocator.allocate_array<MYSQL_BIND>(column_count);
|
||||||
|
|
||||||
|
for (auto i = 0u; i < column_count; i++)
|
||||||
|
{
|
||||||
|
binds[i].length = &real_lengths[i];
|
||||||
|
binds[i].is_null = &is_null[i];
|
||||||
|
binds[i].error = &errors[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mysql_stmt_bind_result(stmt, binds) != 0 ||
|
||||||
|
mysql_stmt_store_result(stmt) != 0)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
while (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
|
||||||
|
{
|
||||||
|
for (auto i = 0u; i < column_count; i++)
|
||||||
|
{
|
||||||
|
switch (fields[i].type)
|
||||||
|
{
|
||||||
|
case enum_field_types::MYSQL_TYPE_INT24:
|
||||||
|
case enum_field_types::MYSQL_TYPE_LONG:
|
||||||
|
case enum_field_types::MYSQL_TYPE_SHORT:
|
||||||
|
{
|
||||||
|
binds[i].buffer_type = MYSQL_TYPE_LONG;
|
||||||
|
binds[i].buffer = allocator.allocate<int>();
|
||||||
|
binds[i].buffer_length = sizeof(int);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
case enum_field_types::MYSQL_TYPE_LONGLONG:
|
||||||
|
{
|
||||||
|
binds[i].buffer_type = MYSQL_TYPE_LONGLONG;
|
||||||
|
binds[i].buffer = allocator.allocate<std::int64_t>();
|
||||||
|
binds[i].buffer_length = sizeof(std::int64_t);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
case enum_field_types::MYSQL_TYPE_FLOAT:
|
||||||
|
binds[i].buffer_type = MYSQL_TYPE_FLOAT;
|
||||||
|
binds[i].buffer = allocator.allocate<float>();
|
||||||
|
binds[i].buffer_length = sizeof(float);
|
||||||
|
break;
|
||||||
|
case enum_field_types::MYSQL_TYPE_DOUBLE:
|
||||||
|
{
|
||||||
|
binds[i].buffer_type = MYSQL_TYPE_DOUBLE;
|
||||||
|
binds[i].buffer = allocator.allocate<double>();
|
||||||
|
binds[i].buffer_length = sizeof(double);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
binds[i].buffer_type = MYSQL_TYPE_STRING;
|
||||||
|
binds[i].buffer = allocator.allocate_array<char>(real_lengths[i]);
|
||||||
|
binds[i].buffer_length = real_lengths[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_stmt_fetch_column(stmt, &binds[i], i, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
scripting::array row_arr;
|
||||||
|
|
||||||
|
for (auto i = 0u; i < column_count; i++)
|
||||||
|
{
|
||||||
|
const auto field = &fields[i];
|
||||||
|
const std::string field_name = {field->name, field->name_length};
|
||||||
|
row_arr[field_name] = bind_to_value(&binds[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
result_arr.emplace_back(row_arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_free_result(meta);
|
||||||
|
|
||||||
|
return result_arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
scripting::array generate_result(MYSQL_RES* result)
|
||||||
|
{
|
||||||
|
if (result == nullptr)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
scripting::array result_arr;
|
||||||
|
|
||||||
|
const auto num_rows = mysql_num_rows(result);
|
||||||
|
const auto num_fields = mysql_num_fields(result);
|
||||||
|
const auto fields = mysql_fetch_fields(result);
|
||||||
|
|
||||||
|
for (auto i = 0u; i < num_rows; i++)
|
||||||
|
{
|
||||||
|
scripting::array row_arr;
|
||||||
|
|
||||||
|
const auto row = mysql_fetch_row(result);
|
||||||
|
const auto lengths = mysql_fetch_lengths(result);
|
||||||
|
|
||||||
|
for (auto f = 0u; f < num_fields; f++)
|
||||||
|
{
|
||||||
|
const auto field = &fields[f];
|
||||||
|
|
||||||
|
const std::string field_str = {field->name, field->name_length};
|
||||||
|
const std::string row_str = {row[f], lengths[f]};
|
||||||
|
const auto value = field_to_value(field, row_str);
|
||||||
|
|
||||||
|
row_arr[field_str] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
result_arr.emplace_back(row_arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result_arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
scripting::object create_mysql_query(F&& cb)
|
||||||
|
{
|
||||||
|
auto task = &tasks[task_index++];
|
||||||
|
|
||||||
|
task->done = false;
|
||||||
|
task->canceled = false;
|
||||||
|
task->handle = std::make_unique<scripting::object>();
|
||||||
|
|
||||||
|
task->thread = std::thread([=]()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mysql::access([&](database_t& db)
|
||||||
|
{
|
||||||
|
task->result = cb(db);
|
||||||
|
task->done = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
printf("%s\n", e.what());
|
||||||
|
task->done = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return *task->handle.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<connection_t, max_connections> connection_pool;
|
||||||
|
|
||||||
|
utils::concurrency::container<sql::connection_config>& get_config()
|
||||||
|
{
|
||||||
|
static utils::concurrency::container<sql::connection_config> config;
|
||||||
|
static auto initialized = false;
|
||||||
|
|
||||||
|
if (!initialized)
|
||||||
|
{
|
||||||
|
config.access([&](sql::connection_config& cfg)
|
||||||
|
{
|
||||||
|
cfg.user = "root";
|
||||||
|
cfg.password = "root";
|
||||||
|
cfg.host = "localhost";
|
||||||
|
cfg.port = 3306;
|
||||||
|
cfg.database = "default";
|
||||||
|
});
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup_connections()
|
||||||
|
{
|
||||||
|
for (auto& connection : connection_pool)
|
||||||
|
{
|
||||||
|
std::unique_lock<database_mutex_t> lock(connection.mutex, std::try_to_lock);
|
||||||
|
if (!lock.owns_lock())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
const auto diff = now - connection.last_access;
|
||||||
|
if (diff >= connection_timeout)
|
||||||
|
{
|
||||||
|
connection.db.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class component final : public component_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void on_shutdown([[maybe_unused]] plugin::plugin* plugin) override
|
||||||
|
{
|
||||||
|
for (auto i = tasks.begin(); i != tasks.end(); ++i)
|
||||||
|
{
|
||||||
|
i->second.canceled = true;
|
||||||
|
|
||||||
|
if (i->second.thread.joinable())
|
||||||
|
{
|
||||||
|
i->second.thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_startup([[maybe_unused]] plugin::plugin* plugin) override
|
||||||
|
{
|
||||||
|
scripting::on_shutdown([]()
|
||||||
|
{
|
||||||
|
for (auto i = tasks.begin(); i != tasks.end(); ++i)
|
||||||
|
{
|
||||||
|
i->second.canceled = true;
|
||||||
|
i->second.handle.reset();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
scheduler::loop([]
|
||||||
|
{
|
||||||
|
cleanup_connections();
|
||||||
|
}, scheduler::async, 1s);
|
||||||
|
|
||||||
|
scheduler::loop([]
|
||||||
|
{
|
||||||
|
for (auto i = tasks.begin(); i != tasks.end(); )
|
||||||
|
{
|
||||||
|
if (!i->second.done)
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i->second.thread.joinable())
|
||||||
|
{
|
||||||
|
i->second.thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!i->second.canceled)
|
||||||
|
{
|
||||||
|
const auto result = i->second.result.result
|
||||||
|
? generate_result(i->second.result.result)
|
||||||
|
: generate_result(i->second.result.stmt);
|
||||||
|
|
||||||
|
const auto rows = static_cast<size_t>(i->second.result.affected_rows);
|
||||||
|
|
||||||
|
scripting::notify(i->second.handle->get_entity_id(), "done", {result, rows, i->second.result.error});
|
||||||
|
|
||||||
|
if (i->second.result.result)
|
||||||
|
{
|
||||||
|
mysql_free_result(i->second.result.result);
|
||||||
|
i->second.result.result = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i->second.result.stmt)
|
||||||
|
{
|
||||||
|
mysql_stmt_close(i->second.result.stmt);
|
||||||
|
i->second.result.stmt = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i = tasks.erase(i);
|
||||||
|
}
|
||||||
|
}, scheduler::server);
|
||||||
|
|
||||||
|
gsc::function::add("mysql::set_config", [](const scripting::object& config)
|
||||||
|
{
|
||||||
|
get_config().access([&](sql::connection_config& cfg)
|
||||||
|
{
|
||||||
|
cfg.host = config["host"].as<std::string>();
|
||||||
|
cfg.user = config["user"].as<std::string>();
|
||||||
|
cfg.password = config["password"].as<std::string>();
|
||||||
|
cfg.port = config["port"].as<unsigned short>();
|
||||||
|
cfg.database = config["database"].as<std::string>();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("mysql::query", [](const std::string& query)
|
||||||
|
{
|
||||||
|
return create_mysql_query([=](database_t& db)
|
||||||
|
{
|
||||||
|
const auto handle = db->get_handle();
|
||||||
|
|
||||||
|
mysql_result_t result{};
|
||||||
|
if (mysql_query(handle, query.data()) != 0)
|
||||||
|
{
|
||||||
|
result.error = mysql_error(handle);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.result = mysql_store_result(handle);
|
||||||
|
result.affected_rows = mysql_affected_rows(handle);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
gsc::function::add("mysql::prepared_statement", [](const std::string& query, const scripting::variadic_args& values)
|
||||||
|
{
|
||||||
|
MYSQL_BIND* binds = nullptr;
|
||||||
|
size_t bind_count = 0;
|
||||||
|
|
||||||
|
const auto free_binds = [=]
|
||||||
|
{
|
||||||
|
if (binds == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0u; i < bind_count; i++)
|
||||||
|
{
|
||||||
|
utils::memory::free(binds[i].buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::memory::free(binds);
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto bind_args = [&]<typename T>(const T& args)
|
||||||
|
{
|
||||||
|
bind_count = args.size();
|
||||||
|
binds = utils::memory::allocate_array<MYSQL_BIND>(bind_count);
|
||||||
|
|
||||||
|
for (auto i = 0u; i < args.size(); i++)
|
||||||
|
{
|
||||||
|
const auto& arg = args[i];
|
||||||
|
const auto& raw_value = arg.get_raw();
|
||||||
|
|
||||||
|
switch (raw_value.type)
|
||||||
|
{
|
||||||
|
case game::SCRIPT_FLOAT:
|
||||||
|
{
|
||||||
|
binds[i].buffer = utils::memory::allocate<float>();
|
||||||
|
binds[i].buffer_type = MYSQL_TYPE_FLOAT;
|
||||||
|
*reinterpret_cast<float*>(binds[i].buffer) = raw_value.u.floatValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case game::SCRIPT_INTEGER:
|
||||||
|
{
|
||||||
|
binds[i].buffer = utils::memory::allocate<int>();
|
||||||
|
binds[i].buffer_type = MYSQL_TYPE_LONG;
|
||||||
|
*reinterpret_cast<int*>(binds[i].buffer) = raw_value.u.intValue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case game::SCRIPT_STRING:
|
||||||
|
{
|
||||||
|
const auto str = arg.as<std::string>();
|
||||||
|
const auto str_copy = utils::memory::duplicate_string(str);
|
||||||
|
binds[i].buffer = str_copy;
|
||||||
|
binds[i].buffer_length = str.size();
|
||||||
|
binds[i].buffer_type = MYSQL_TYPE_STRING;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
binds[i].buffer_type = MYSQL_TYPE_NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (values.size() > 0 && values[0].is<scripting::array>())
|
||||||
|
{
|
||||||
|
bind_args(values[0].as<scripting::array>());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bind_args(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
free_binds();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_mysql_query([=](database_t& db)
|
||||||
|
{
|
||||||
|
const auto _0 = gsl::finally([&]
|
||||||
|
{
|
||||||
|
free_binds();
|
||||||
|
});
|
||||||
|
|
||||||
|
mysql_result_t result{};
|
||||||
|
|
||||||
|
const auto handle = db->get_handle();
|
||||||
|
const auto stmt = mysql_stmt_init(handle);
|
||||||
|
|
||||||
|
if (mysql_stmt_prepare(stmt, query.data(), query.size()) != 0 ||
|
||||||
|
mysql_stmt_bind_param(stmt, binds) != 0 ||
|
||||||
|
mysql_stmt_execute(stmt) != 0)
|
||||||
|
{
|
||||||
|
result.error = mysql_stmt_error(stmt);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.stmt = stmt;
|
||||||
|
result.affected_rows = mysql_stmt_affected_rows(stmt);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(mysql::component)
|
70
src/component/mysql.hpp
Normal file
70
src/component/mysql.hpp
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utils/concurrency.hpp>
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4127)
|
||||||
|
#pragma warning(disable: 4267)
|
||||||
|
#pragma warning(disable: 4018)
|
||||||
|
#pragma warning(disable: 4996)
|
||||||
|
#pragma warning(disable: 4244)
|
||||||
|
#include <sqlpp11/sqlpp11.h>
|
||||||
|
#include <sqlpp11/mysql/mysql.h>
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
namespace sql = sqlpp::mysql;
|
||||||
|
|
||||||
|
namespace mysql
|
||||||
|
{
|
||||||
|
constexpr auto max_connections = 256;
|
||||||
|
constexpr auto connection_timeout = 200s;
|
||||||
|
|
||||||
|
using database_mutex_t = std::recursive_mutex;
|
||||||
|
using database_t = std::unique_ptr<sql::connection>;
|
||||||
|
|
||||||
|
struct connection_t
|
||||||
|
{
|
||||||
|
database_t db;
|
||||||
|
database_mutex_t mutex;
|
||||||
|
std::chrono::high_resolution_clock::time_point start;
|
||||||
|
std::chrono::high_resolution_clock::time_point last_access;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern std::array<connection_t, max_connections> connection_pool;
|
||||||
|
|
||||||
|
utils::concurrency::container<sql::connection_config>& get_config();
|
||||||
|
|
||||||
|
template <typename T = void, typename F>
|
||||||
|
T access(F&& accessor)
|
||||||
|
{
|
||||||
|
for (auto& connection : connection_pool)
|
||||||
|
{
|
||||||
|
std::unique_lock<database_mutex_t> lock(connection.mutex, std::try_to_lock);
|
||||||
|
if (!lock.owns_lock())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
const auto diff = now - connection.start;
|
||||||
|
|
||||||
|
if (!connection.db.get() || !connection.db->ping_server() || diff >= 1h)
|
||||||
|
{
|
||||||
|
get_config().access([&](sql::connection_config& cfg)
|
||||||
|
{
|
||||||
|
connection.db = std::make_unique<sql::connection>(cfg);
|
||||||
|
connection.start = now;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.last_access = now;
|
||||||
|
return accessor(connection.db);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("out of connections");
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup_connections();
|
||||||
|
|
||||||
|
void run_tasks();
|
||||||
|
}
|
@@ -1,14 +1,8 @@
|
|||||||
#include <stdinc.hpp>
|
#include <stdinc.hpp>
|
||||||
#include "loader/component_loader.hpp"
|
#include "loader/component_loader.hpp"
|
||||||
|
|
||||||
#include "game/structs.hpp"
|
|
||||||
#include "game/game.hpp"
|
|
||||||
|
|
||||||
#include "gsc.hpp"
|
#include "gsc.hpp"
|
||||||
|
|
||||||
#include <utils/io.hpp>
|
|
||||||
#include <uchar.h>
|
|
||||||
|
|
||||||
// lua/lstrlib.c
|
// lua/lstrlib.c
|
||||||
#define MAX_FORMAT 32
|
#define MAX_FORMAT 32
|
||||||
#define L_FMTFLAGSF "-+#0 "
|
#define L_FMTFLAGSF "-+#0 "
|
||||||
@@ -110,13 +104,13 @@ namespace string
|
|||||||
case 'X':
|
case 'X':
|
||||||
flags = L_FMTFLAGSX;
|
flags = L_FMTFLAGSX;
|
||||||
intcase:
|
intcase:
|
||||||
{
|
{
|
||||||
check_format(form, flags, 1);
|
check_format(form, flags, 1);
|
||||||
const auto value = va[va_index].as<int>();
|
const auto value = va[va_index].as<int>();
|
||||||
buffer.append(utils::string::va(form, value));
|
buffer.append(utils::string::va(form, value));
|
||||||
va_index++;
|
va_index++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'f':
|
case 'f':
|
||||||
case 'F':
|
case 'F':
|
||||||
case 'e':
|
case 'e':
|
||||||
@@ -157,6 +151,7 @@ namespace string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -205,7 +200,7 @@ namespace string
|
|||||||
{
|
{
|
||||||
for (const auto& s : match)
|
for (const auto& s : match)
|
||||||
{
|
{
|
||||||
array_match.push((s.str()));
|
array_match.emplace_back((s.str()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,8 +6,30 @@
|
|||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/binary_resource.hpp>
|
||||||
#include <utils/nt.hpp>
|
#include <utils/nt.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
utils::hook::detour load_library_hook;
|
||||||
|
HMODULE __stdcall load_library_stub(LPCSTR lib_name, HANDLE file, DWORD flags)
|
||||||
|
{
|
||||||
|
if (lib_name == "libmysql.dll"s)
|
||||||
|
{
|
||||||
|
static auto dll = utils::binary_resource{LIBMYSQL_DLL, lib_name};
|
||||||
|
const auto path = dll.get_extracted_file();
|
||||||
|
const auto handle = load_library_hook.invoke_pascal<HMODULE>(path.data(), file, flags);
|
||||||
|
|
||||||
|
if (handle != nullptr)
|
||||||
|
{
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return load_library_hook.invoke_pascal<HMODULE>(lib_name, file, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PLUTONIUM_API plutonium::sdk::plugin* PLUTONIUM_CALLBACK on_initialize()
|
PLUTONIUM_API plutonium::sdk::plugin* PLUTONIUM_CALLBACK on_initialize()
|
||||||
{
|
{
|
||||||
return plugin::get();
|
return plugin::get();
|
||||||
@@ -18,6 +40,7 @@ BOOL APIENTRY DllMain(HMODULE module, DWORD ul_reason_for_call, LPVOID /*reserve
|
|||||||
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
|
||||||
{
|
{
|
||||||
utils::nt::library::set_current_handle(module);
|
utils::nt::library::set_current_handle(module);
|
||||||
|
load_library_hook.create(LoadLibraryExA, load_library_stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ul_reason_for_call == DLL_PROCESS_DETACH)
|
if (ul_reason_for_call == DLL_PROCESS_DETACH)
|
||||||
|
@@ -55,70 +55,21 @@ namespace scripting
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
array_value::array_value(unsigned int parent_id, unsigned int id)
|
array_value::array_value(const array* array, const script_value& key)
|
||||||
: id_(id)
|
: array_(array)
|
||||||
, parent_id_(parent_id)
|
, key_(key)
|
||||||
{
|
{
|
||||||
if (!this->id_)
|
const auto value = this->array_->get(key);
|
||||||
{
|
this->script_value::operator=(value);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
game::VariableValue variable_{};
|
|
||||||
|
|
||||||
if (game::environment::is_sp())
|
|
||||||
{
|
|
||||||
const auto variable = &game::scr_VarGlob->variableList_sp[this->id_ + 0x6000];
|
|
||||||
variable_.type = variable->w.type & 0x1F;
|
|
||||||
variable_.u = variable->u.u;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto variable = &game::scr_VarGlob->variableList_mp[this->id_ + 0x8000];
|
|
||||||
variable_.type = variable->w.type & 0x1F;
|
|
||||||
variable_.u = variable->u.u;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->value_ = variable_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void array_value::operator=(const script_value& value)
|
void array_value::operator=(const script_value& value)
|
||||||
{
|
{
|
||||||
if (!this->id_)
|
this->array_->set(this->key_, value);
|
||||||
{
|
this->script_value::operator=(value);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& value_0 = value.get_raw();
|
|
||||||
|
|
||||||
game::VariableValue previous{};
|
|
||||||
|
|
||||||
if (game::environment::is_sp())
|
|
||||||
{
|
|
||||||
const auto variable = &game::scr_VarGlob->variableList_sp[this->id_ + 0x6000];
|
|
||||||
previous.type = variable->w.type & 0x1F;
|
|
||||||
previous.u = variable->u.u;
|
|
||||||
|
|
||||||
variable->w.type |= value_0.type;
|
|
||||||
variable->u.u = value_0.u;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto variable = &game::scr_VarGlob->variableList_mp[this->id_ + 0x8000];
|
|
||||||
previous.type = variable->w.type & 0x1F;
|
|
||||||
previous.u = variable->u.u;
|
|
||||||
|
|
||||||
variable->w.type |= value_0.type;
|
|
||||||
variable->u.u = value_0.u;
|
|
||||||
}
|
|
||||||
|
|
||||||
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value_0);
|
|
||||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, previous.type, previous.u);
|
|
||||||
|
|
||||||
this->value_ = value_0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
array::array(const unsigned int id)
|
array::array(const std::uint32_t id)
|
||||||
: id_(id)
|
: id_(id)
|
||||||
{
|
{
|
||||||
this->add();
|
this->add();
|
||||||
@@ -198,18 +149,18 @@ namespace scripting
|
|||||||
return SELECT_VALUE(get_keys_sp, get_keys_mp)(this->id_);
|
return SELECT_VALUE(get_keys_sp, get_keys_mp)(this->id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
int array::size() const
|
std::uint32_t array::size() const
|
||||||
{
|
{
|
||||||
return static_cast<int>(game::Scr_GetSelf(game::SCRIPTINSTANCE_SERVER, this->id_));
|
return static_cast<int>(game::Scr_GetSelf(game::SCRIPTINSTANCE_SERVER, this->id_));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int array::push(const script_value& value) const
|
std::uint32_t array::push_back(const script_value& value) const
|
||||||
{
|
{
|
||||||
this->set(this->size(), value);
|
this->set(this->size(), value);
|
||||||
return this->size();
|
return this->size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void array::erase(const unsigned int index) const
|
void array::erase(const std::uint32_t index) const
|
||||||
{
|
{
|
||||||
const auto variable_id = game::FindArrayVariable(game::SCRIPTINSTANCE_SERVER, this->id_, index);
|
const auto variable_id = game::FindArrayVariable(game::SCRIPTINSTANCE_SERVER, this->id_, index);
|
||||||
if (variable_id)
|
if (variable_id)
|
||||||
@@ -228,13 +179,6 @@ namespace scripting
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
script_value array::pop() const
|
|
||||||
{
|
|
||||||
const auto value = this->get(this->size() - 1);
|
|
||||||
this->erase(this->size() - 1);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value array::get(const std::string& key) const
|
script_value array::get(const std::string& key) const
|
||||||
{
|
{
|
||||||
const auto string_value = game::SL_GetString(key.data(), 0, game::SCRIPTINSTANCE_SERVER);
|
const auto string_value = game::SL_GetString(key.data(), 0, game::SCRIPTINSTANCE_SERVER);
|
||||||
@@ -263,6 +207,24 @@ namespace scripting
|
|||||||
return variable_;
|
return variable_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void array::erase(const script_value& key) const
|
||||||
|
{
|
||||||
|
if (key.is<int>())
|
||||||
|
{
|
||||||
|
return this->erase(key.as<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.is<std::string>())
|
||||||
|
{
|
||||||
|
return this->erase(key.as<std::string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void array::erase(const array_iterator& iter) const
|
||||||
|
{
|
||||||
|
this->erase(iter->first);
|
||||||
|
}
|
||||||
|
|
||||||
script_value array::get(const unsigned int index) const
|
script_value array::get(const unsigned int index) const
|
||||||
{
|
{
|
||||||
const auto variable_id = game::FindArrayVariable(game::SCRIPTINSTANCE_SERVER, this->id_, index);
|
const auto variable_id = game::FindArrayVariable(game::SCRIPTINSTANCE_SERVER, this->id_, index);
|
||||||
@@ -294,15 +256,15 @@ namespace scripting
|
|||||||
{
|
{
|
||||||
if (key.is<int>())
|
if (key.is<int>())
|
||||||
{
|
{
|
||||||
this->get(key.as<int>());
|
return this->get(key.as<int>());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.is<std::string>())
|
if (key.is<std::string>())
|
||||||
{
|
{
|
||||||
this->get(key.as<std::string>());
|
return this->get(key.as<std::string>());
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
throw std::runtime_error(std::format("invalid key type '{}'", key.type_name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void array::set(const std::string& key, const script_value& value) const
|
void array::set(const std::string& key, const script_value& value) const
|
||||||
@@ -340,7 +302,7 @@ namespace scripting
|
|||||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, previous.type, previous.u);
|
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, previous.type, previous.u);
|
||||||
}
|
}
|
||||||
|
|
||||||
void array::set(const unsigned int index, const script_value& value) const
|
void array::set(const std::uint32_t index, const script_value& value) const
|
||||||
{
|
{
|
||||||
const auto& value_ = value.get_raw();
|
const auto& value_ = value.get_raw();
|
||||||
const auto variable_id = this->get_value_id(index);
|
const auto variable_id = this->get_value_id(index);
|
||||||
@@ -375,25 +337,27 @@ namespace scripting
|
|||||||
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, previous.type, previous.u);
|
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, previous.type, previous.u);
|
||||||
}
|
}
|
||||||
|
|
||||||
void array::set(const script_value& key, const script_value& _value) const
|
void array::set(const script_value& key, const script_value& value) const
|
||||||
{
|
{
|
||||||
if (key.is<int>())
|
if (key.is<int>())
|
||||||
{
|
{
|
||||||
this->set(key.as<int>(), _value);
|
return this->set(key.as<int>(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.is<std::string>())
|
if (key.is<std::string>())
|
||||||
{
|
{
|
||||||
this->set(key.as<std::string>(), _value);
|
return this->set(key.as<std::string>(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error(std::format("invalid key type '{}'", key.type_name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int array::get_entity_id() const
|
std::uint32_t array::get_entity_id() const
|
||||||
{
|
{
|
||||||
return this->id_;
|
return this->id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int array::get_value_id(const std::string& key) const
|
std::uint32_t array::get_value_id(const std::string& key) const
|
||||||
{
|
{
|
||||||
const auto string_value = game::SL_GetString(key.data(), 0, game::SCRIPTINSTANCE_SERVER);
|
const auto string_value = game::SL_GetString(key.data(), 0, game::SCRIPTINSTANCE_SERVER);
|
||||||
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
|
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
|
||||||
@@ -406,7 +370,7 @@ namespace scripting
|
|||||||
return variable_id;
|
return variable_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int array::get_value_id(const unsigned int index) const
|
std::uint32_t array::get_value_id(const std::uint32_t index) const
|
||||||
{
|
{
|
||||||
const auto variable_id = game::FindArrayVariable(game::SCRIPTINSTANCE_SERVER, this->id_, index);
|
const auto variable_id = game::FindArrayVariable(game::SCRIPTINSTANCE_SERVER, this->id_, index);
|
||||||
if (!variable_id)
|
if (!variable_id)
|
||||||
@@ -421,4 +385,34 @@ namespace scripting
|
|||||||
{
|
{
|
||||||
return entity(this->id_);
|
return entity(this->id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
array_value array::operator[](const script_value& key) const
|
||||||
|
{
|
||||||
|
return array_value(this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
array_iterator array::begin() const
|
||||||
|
{
|
||||||
|
const auto keys = this->get_keys();
|
||||||
|
return array_iterator(this, keys, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
array_iterator array::end() const
|
||||||
|
{
|
||||||
|
return array_iterator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
array_iterator array::find(const script_value& key) const
|
||||||
|
{
|
||||||
|
const auto keys = this->get_keys();
|
||||||
|
for (auto i = 0u; i < keys.size(); i++)
|
||||||
|
{
|
||||||
|
if (keys[i] == key)
|
||||||
|
{
|
||||||
|
return array_iterator(this, keys, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_iterator(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,23 +1,57 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "script_value.hpp"
|
#include "script_value.hpp"
|
||||||
|
#include "container_iterator.hpp"
|
||||||
|
|
||||||
namespace scripting
|
namespace scripting
|
||||||
{
|
{
|
||||||
class array_value : public script_value
|
class array_value : public script_value
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
array_value(unsigned int, unsigned int);
|
array_value(const array* array, const script_value& key);
|
||||||
void operator=(const script_value&);
|
void operator=(const script_value& value);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T as() const
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return script_value::as<T>();
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::format("array value '{}' {}", this->key_.to_string(), e.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned int id_;
|
script_value key_;
|
||||||
unsigned int parent_id_;
|
const array* array_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename IteratorType>
|
||||||
|
class array_iterator_base : public IteratorType
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
array_iterator_base(const array* container)
|
||||||
|
: IteratorType(container)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
array_iterator_base(const array* container, const std::vector<script_value>& keys, const std::int64_t key_index)
|
||||||
|
: IteratorType(container, keys, key_index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using array_iterator = array_iterator_base<container_iterator<array, script_value, array_value>>;
|
||||||
|
|
||||||
class array final
|
class array final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
array();
|
array();
|
||||||
array(const unsigned int);
|
array(const std::uint32_t id);
|
||||||
|
|
||||||
array(const array& other);
|
array(const array& other);
|
||||||
array(array&& other) noexcept;
|
array(array&& other) noexcept;
|
||||||
@@ -28,58 +62,46 @@ namespace scripting
|
|||||||
array& operator=(array&& other) noexcept;
|
array& operator=(array&& other) noexcept;
|
||||||
|
|
||||||
std::vector<script_value> get_keys() const;
|
std::vector<script_value> get_keys() const;
|
||||||
int size() const;
|
std::uint32_t size() const;
|
||||||
|
|
||||||
unsigned int push(const script_value&) const;
|
std::uint32_t push_back(const script_value& value) const;
|
||||||
void erase(const unsigned int) const;
|
|
||||||
void erase(const std::string&) const;
|
template <typename ...Args>
|
||||||
script_value pop() const;
|
void emplace_back(Args&&... args)
|
||||||
|
{
|
||||||
|
this->push_back(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase(const std::uint32_t index) const;
|
||||||
|
void erase(const std::string& key) const;
|
||||||
|
void erase(const script_value& key) const;
|
||||||
|
void erase(const array_iterator& iter) const;
|
||||||
|
|
||||||
script_value get(const script_value&) const;
|
script_value get(const script_value&) const;
|
||||||
script_value get(const std::string&) const;
|
script_value get(const std::string&) const;
|
||||||
script_value get(const unsigned int) const;
|
script_value get(const std::uint32_t) const;
|
||||||
|
|
||||||
void set(const script_value&, const script_value&) const;
|
void set(const script_value&, const script_value&) const;
|
||||||
void set(const std::string&, const script_value&) const;
|
void set(const std::string&, const script_value&) const;
|
||||||
void set(const unsigned int, const script_value&) const;
|
void set(const std::uint32_t, const script_value&) const;
|
||||||
|
|
||||||
unsigned int get_entity_id() const;
|
std::uint32_t get_entity_id() const;
|
||||||
|
|
||||||
unsigned int get_value_id(const std::string&) const;
|
std::uint32_t get_value_id(const std::string&) const;
|
||||||
unsigned int get_value_id(const unsigned int) const;
|
std::uint32_t get_value_id(const std::uint32_t) const;
|
||||||
|
|
||||||
entity get_raw() const;
|
entity get_raw() const;
|
||||||
|
|
||||||
array_value operator[](const int index) const
|
array_value operator[](const script_value& key) const;
|
||||||
{
|
|
||||||
return {this->id_, this->get_value_id(index)};
|
|
||||||
}
|
|
||||||
|
|
||||||
array_value operator[](const std::string& key) const
|
array_iterator begin() const;
|
||||||
{
|
array_iterator end() const;
|
||||||
return {this->id_, this->get_value_id(key)};
|
array_iterator find(const script_value& key) const;
|
||||||
}
|
|
||||||
|
|
||||||
template <typename I = int, typename S = std::string>
|
|
||||||
array_value operator[](const script_value& key) const
|
|
||||||
{
|
|
||||||
if (key.is<I>())
|
|
||||||
{
|
|
||||||
return {this->id_, this->get_value_id(key.as<I>())};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key.is<S>())
|
|
||||||
{
|
|
||||||
return {this->id_, this->get_value_id(key.as<S>())};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("Invalid key type");
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void add() const;
|
void add() const;
|
||||||
void release() const;
|
void release() const;
|
||||||
|
|
||||||
unsigned int id_{};
|
std::uint32_t id_{};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
99
src/game/scripting/container_iterator.hpp
Normal file
99
src/game/scripting/container_iterator.hpp
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "script_value.hpp"
|
||||||
|
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
template <typename ContainerType, typename KeyType, typename ValueType>
|
||||||
|
class container_iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr std::int64_t container_iterator_end_key = -1;
|
||||||
|
|
||||||
|
container_iterator(const ContainerType* container)
|
||||||
|
: container_(container)
|
||||||
|
, key_index_(container_iterator_end_key)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
container_iterator(const ContainerType* container, const std::vector<KeyType>& keys, const std::int64_t key_index)
|
||||||
|
: container_(container)
|
||||||
|
, keys_(keys)
|
||||||
|
, key_index_(key_index)
|
||||||
|
{
|
||||||
|
this->update_pair();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<KeyType, ValueType>& operator*()
|
||||||
|
{
|
||||||
|
return this->pair_.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<KeyType, ValueType>* operator->()
|
||||||
|
{
|
||||||
|
return &this->pair_.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::pair<KeyType, ValueType>& operator*() const
|
||||||
|
{
|
||||||
|
return this->pair_.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::pair<KeyType, ValueType>* operator->() const
|
||||||
|
{
|
||||||
|
return &this->pair_.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
container_iterator& operator++()
|
||||||
|
{
|
||||||
|
if (this->key_index_ == container_iterator_end_key)
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto size = this->keys_.size();
|
||||||
|
if (this->key_index_ + 1 >= size)
|
||||||
|
{
|
||||||
|
this->key_index_ = container_iterator_end_key;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->key_index_++;
|
||||||
|
this->update_pair();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
container_iterator operator++(int)
|
||||||
|
{
|
||||||
|
const auto pre = *this;
|
||||||
|
this->operator++();
|
||||||
|
return pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(const container_iterator& a, const container_iterator& b)
|
||||||
|
{
|
||||||
|
return a.container_ == b.container_ && a.key_index_ == b.key_index_;
|
||||||
|
};
|
||||||
|
|
||||||
|
friend bool operator!=(const container_iterator& a, const container_iterator& b)
|
||||||
|
{
|
||||||
|
return a.container_ != b.container_ || a.key_index_ != b.key_index_;
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
void update_pair()
|
||||||
|
{
|
||||||
|
const auto index = static_cast<size_t>(this->key_index_);
|
||||||
|
const auto& key = this->keys_[index];
|
||||||
|
const auto value = this->container_->operator[](key);
|
||||||
|
this->pair_.emplace(std::make_pair(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ContainerType* container_;
|
||||||
|
std::int64_t key_index_;
|
||||||
|
std::vector<KeyType> keys_;
|
||||||
|
std::optional<std::pair<KeyType, ValueType>> pair_;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
@@ -147,12 +147,30 @@ namespace scripting
|
|||||||
return this->is<int>();
|
return this->is<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<unsigned short>() const
|
||||||
|
{
|
||||||
|
return this->is<int>();
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
bool script_value::is<bool>() const
|
bool script_value::is<bool>() const
|
||||||
{
|
{
|
||||||
return this->is<int>();
|
return this->is<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<short>() const
|
||||||
|
{
|
||||||
|
return this->is<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<char>() const
|
||||||
|
{
|
||||||
|
return this->is<int>();
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
int script_value::get() const
|
int script_value::get() const
|
||||||
{
|
{
|
||||||
@@ -171,6 +189,24 @@ namespace scripting
|
|||||||
return this->get_raw().u.uintValue != 0;
|
return this->get_raw().u.uintValue != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
unsigned short script_value::get() const
|
||||||
|
{
|
||||||
|
return static_cast<unsigned short>(this->get_raw().u.uintValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
short script_value::get() const
|
||||||
|
{
|
||||||
|
return static_cast<short>(this->get_raw().u.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
char script_value::get() const
|
||||||
|
{
|
||||||
|
return static_cast<char>(this->get_raw().u.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
/***********************************************
|
/***********************************************
|
||||||
* Float
|
* Float
|
||||||
**********************************************/
|
**********************************************/
|
||||||
@@ -178,7 +214,8 @@ namespace scripting
|
|||||||
template <>
|
template <>
|
||||||
bool script_value::is<float>() const
|
bool script_value::is<float>() const
|
||||||
{
|
{
|
||||||
return this->get_raw().type == game::SCRIPT_FLOAT;
|
const auto type = this->get_raw().type;
|
||||||
|
return type == game::SCRIPT_FLOAT || type == game::SCRIPT_INTEGER;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
@@ -190,12 +227,24 @@ namespace scripting
|
|||||||
template <>
|
template <>
|
||||||
float script_value::get() const
|
float script_value::get() const
|
||||||
{
|
{
|
||||||
|
const auto type = this->get_raw().type;
|
||||||
|
if (type == game::SCRIPT_INTEGER)
|
||||||
|
{
|
||||||
|
return static_cast<float>(this->get_raw().u.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
return this->get_raw().u.floatValue;
|
return this->get_raw().u.floatValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
double script_value::get() const
|
double script_value::get() const
|
||||||
{
|
{
|
||||||
|
const auto type = this->get_raw().type;
|
||||||
|
if (type == game::SCRIPT_INTEGER)
|
||||||
|
{
|
||||||
|
return static_cast<double>(this->get_raw().u.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
return static_cast<double>(this->get_raw().u.floatValue);
|
return static_cast<double>(this->get_raw().u.floatValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,6 +369,12 @@ namespace scripting
|
|||||||
return this->get_raw().type == game::SCRIPT_VECTOR;
|
return this->get_raw().type == game::SCRIPT_VECTOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<float*>() const
|
||||||
|
{
|
||||||
|
return this->is<vector>();
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
vector script_value::get() const
|
vector script_value::get() const
|
||||||
{
|
{
|
||||||
@@ -413,4 +468,24 @@ namespace scripting
|
|||||||
|
|
||||||
return std::vector<function_argument>::operator[](index);
|
return std::vector<function_argument>::operator[](index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function_argument function_arguments::operator[](const size_t index) const
|
||||||
|
{
|
||||||
|
if (index >= values_.size())
|
||||||
|
{
|
||||||
|
return {values_, {}, index, false};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {values_, values_[index], index, true};
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments function_arguments::get_raw() const
|
||||||
|
{
|
||||||
|
return this->values_;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t function_arguments::size() const
|
||||||
|
{
|
||||||
|
return this->values_.size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -137,9 +137,20 @@ namespace scripting
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
T get() const
|
T get() const
|
||||||
{
|
{
|
||||||
if (std::is_constructible<T, std::string>::value && this->is<std::string>()) \
|
if constexpr (std::is_pointer<T>::value)
|
||||||
{
|
{
|
||||||
return T(this->as<std::string>());
|
if (this->is<unsigned int>())
|
||||||
|
{
|
||||||
|
return reinterpret_cast<T>(this->as<unsigned int>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_constructible<T, std::string>::value)
|
||||||
|
{
|
||||||
|
if (this->is<std::string>())
|
||||||
|
{
|
||||||
|
return T(this->as<std::string>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("Invalid type");
|
throw std::runtime_error("Invalid type");
|
||||||
@@ -157,7 +168,15 @@ public: \
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
bool is() const
|
bool is() const
|
||||||
{
|
{
|
||||||
if (std::is_constructible<T, std::string>::value && this->is<std::string>()) \
|
if constexpr (std::is_pointer<T>::value)
|
||||||
|
{
|
||||||
|
if (this->is<unsigned int>())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::is_constructible<T, std::string>::value && this->is<std::string>())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -168,7 +187,9 @@ public: \
|
|||||||
ADD_TYPE(bool)
|
ADD_TYPE(bool)
|
||||||
ADD_TYPE(int)
|
ADD_TYPE(int)
|
||||||
ADD_TYPE(unsigned int)
|
ADD_TYPE(unsigned int)
|
||||||
|
ADD_TYPE(unsigned short)
|
||||||
ADD_TYPE(float)
|
ADD_TYPE(float)
|
||||||
|
ADD_TYPE(float*)
|
||||||
ADD_TYPE(double)
|
ADD_TYPE(double)
|
||||||
ADD_TYPE(const char*)
|
ADD_TYPE(const char*)
|
||||||
ADD_TYPE(std::string)
|
ADD_TYPE(std::string)
|
||||||
@@ -193,6 +214,17 @@ public: \
|
|||||||
return get<T>();
|
return get<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T as_or(const T& default_value) const
|
||||||
|
{
|
||||||
|
if (!this->is<T>())
|
||||||
|
{
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return get<T>();
|
||||||
|
}
|
||||||
|
|
||||||
std::string type_name() const
|
std::string type_name() const
|
||||||
{
|
{
|
||||||
return get_typename(this->get_raw());
|
return get_typename(this->get_raw());
|
||||||
@@ -205,7 +237,7 @@ public: \
|
|||||||
|
|
||||||
for (const auto& value : container)
|
for (const auto& value : container)
|
||||||
{
|
{
|
||||||
array_.push(value);
|
array_.emplace_back(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
game::VariableValue value{};
|
game::VariableValue value{};
|
||||||
@@ -223,6 +255,14 @@ public: \
|
|||||||
|
|
||||||
std::string to_string() const;
|
std::string to_string() const;
|
||||||
|
|
||||||
|
friend bool operator==(const script_value& a, const script_value& b)
|
||||||
|
{
|
||||||
|
const auto& value_raw_a = a.get_raw();
|
||||||
|
const auto& value_raw_b = b.get_raw();
|
||||||
|
|
||||||
|
return value_raw_a.type != value_raw_b.type && value_raw_a.u.uintValue == value_raw_b.u.uintValue;
|
||||||
|
}
|
||||||
|
|
||||||
const game::VariableValue& get_raw() const;
|
const game::VariableValue& get_raw() const;
|
||||||
|
|
||||||
variable_value value_{};
|
variable_value value_{};
|
||||||
@@ -265,14 +305,29 @@ public: \
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T, typename I = int>
|
||||||
|
T* as_ptr() const
|
||||||
|
{
|
||||||
|
const auto value = script_value::as<I>();
|
||||||
|
|
||||||
|
if (value == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
return reinterpret_cast<T*>(value);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
variadic_args as() const
|
variadic_args as() const
|
||||||
{
|
{
|
||||||
variadic_args args{this->index_};
|
variadic_args args{this->index_};
|
||||||
|
|
||||||
for (auto i = this->index_; i < this->values_.size(); i++)
|
for (auto i = this->index_; i < this->values_.size(); i++)
|
||||||
{
|
{
|
||||||
args.push_back({this->values_, this->values_[i], i, true});
|
args.emplace_back(this->values_, this->values_[i], i, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,7 +340,7 @@ public: \
|
|||||||
operator C<T, std::allocator<T>>() const
|
operator C<T, std::allocator<T>>() const
|
||||||
{
|
{
|
||||||
const auto container_type = get_c_typename<C<T, std::allocator<T>>>();
|
const auto container_type = get_c_typename<C<T, std::allocator<T>>>();
|
||||||
if (!script_value::as<ArrayType>())
|
if (!script_value::is<ArrayType>())
|
||||||
{
|
{
|
||||||
const auto type = get_typename(this->get_raw());
|
const auto type = get_typename(this->get_raw());
|
||||||
|
|
||||||
@@ -297,11 +352,11 @@ public: \
|
|||||||
|
|
||||||
C<T, std::allocator<T>> container{};
|
C<T, std::allocator<T>> container{};
|
||||||
const auto array = script_value::as<ArrayType>();
|
const auto array = script_value::as<ArrayType>();
|
||||||
for (auto i = 0; i < array.size(); i++)
|
for (auto i = 0u; i < array.size(); i++)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
container.push_back(array.get(i).as<T>());
|
container.emplace_back(array.get(i).as<T>());
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
@@ -323,6 +378,7 @@ public: \
|
|||||||
arguments values_{};
|
arguments values_{};
|
||||||
size_t index_{};
|
size_t index_{};
|
||||||
bool exists_{};
|
bool exists_{};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class function_arguments
|
class function_arguments
|
||||||
@@ -330,16 +386,14 @@ public: \
|
|||||||
public:
|
public:
|
||||||
function_arguments(const arguments& values);
|
function_arguments(const arguments& values);
|
||||||
|
|
||||||
function_argument operator[](const size_t index) const
|
function_argument operator[](const size_t index) const;
|
||||||
{
|
|
||||||
if (index >= values_.size())
|
arguments get_raw() const;
|
||||||
{
|
|
||||||
return {values_, {}, index, false};
|
size_t size() const;
|
||||||
}
|
|
||||||
|
|
||||||
return {values_, values_[index], index, true};
|
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
arguments values_{};
|
arguments values_{};
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -45,11 +45,11 @@ namespace plugin
|
|||||||
{
|
{
|
||||||
this->interface_ = interface_ptr;
|
this->interface_ = interface_ptr;
|
||||||
this->game_ = game;
|
this->game_ = game;
|
||||||
//utils::hook::jump(reinterpret_cast<uintptr_t>(&printf), printf_stub);
|
utils::hook::jump(reinterpret_cast<uintptr_t>(&printf), printf_stub);
|
||||||
|
|
||||||
component_loader::on_startup();
|
component_loader::on_startup();
|
||||||
//interface_ptr->callbacks()->on_dvar_init(&component_loader::on_dvar_init);
|
interface_ptr->callbacks()->on_dvar_init(&component_loader::on_dvar_init);
|
||||||
//interface_ptr->callbacks()->on_after_dvar_init(&component_loader::on_after_dvar_init);
|
interface_ptr->callbacks()->on_after_dvar_init(&component_loader::on_after_dvar_init);
|
||||||
}
|
}
|
||||||
|
|
||||||
void plugin::on_shutdown()
|
void plugin::on_shutdown()
|
||||||
|
3
src/resource.hpp
Normal file
3
src/resource.hpp
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define LIBMYSQL_DLL 100
|
3
src/resource.rc
Normal file
3
src/resource.rc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#include "resource.hpp"
|
||||||
|
|
||||||
|
LIBMYSQL_DLL RCDATA "../../deps/mysql/lib/libmysql.dll"
|
@@ -52,5 +52,9 @@
|
|||||||
#pragma comment(lib, "urlmon.lib" )
|
#pragma comment(lib, "urlmon.lib" )
|
||||||
#pragma comment(lib, "iphlpapi.lib")
|
#pragma comment(lib, "iphlpapi.lib")
|
||||||
#pragma comment(lib, "Crypt32.lib")
|
#pragma comment(lib, "Crypt32.lib")
|
||||||
|
#pragma comment(lib, "libmysql.lib")
|
||||||
|
#pragma comment(lib, "delayimp.lib")
|
||||||
|
|
||||||
|
#include "resource.hpp"
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
76
src/utils/binary_resource.cpp
Normal file
76
src/utils/binary_resource.cpp
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#include <stdinc.hpp>
|
||||||
|
#include "binary_resource.hpp"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include "nt.hpp"
|
||||||
|
#include "io.hpp"
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string get_temp_folder()
|
||||||
|
{
|
||||||
|
char path[MAX_PATH] = {0};
|
||||||
|
if (!GetTempPathA(sizeof(path), path))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Unable to get temp path");
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string write_exitisting_temp_file(const std::string& file, const std::string& data,
|
||||||
|
const bool fatal_if_overwrite_fails)
|
||||||
|
{
|
||||||
|
const auto temp = get_temp_folder();
|
||||||
|
auto file_path = temp + file;
|
||||||
|
|
||||||
|
std::string current_data;
|
||||||
|
if (!io::read_file(file_path, ¤t_data))
|
||||||
|
{
|
||||||
|
if (!io::write_file(file_path, data))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to write file: " + file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return file_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_data == data || io::write_file(file_path, data) || !fatal_if_overwrite_fails)
|
||||||
|
{
|
||||||
|
return file_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Temporary file was already written, but differs. It can't be overwritten as it's still in use: " +
|
||||||
|
file_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binary_resource::binary_resource(const int id, std::string file)
|
||||||
|
: filename_(std::move(file))
|
||||||
|
{
|
||||||
|
this->resource_ = nt::load_resource(id);
|
||||||
|
|
||||||
|
if (this->resource_.empty())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Unable to load resource: " + std::to_string(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string binary_resource::get_extracted_file(const bool fatal_if_overwrite_fails)
|
||||||
|
{
|
||||||
|
if (this->path_.empty())
|
||||||
|
{
|
||||||
|
this->path_ = write_exitisting_temp_file(this->filename_, this->resource_, fatal_if_overwrite_fails);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->path_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& binary_resource::get_data() const
|
||||||
|
{
|
||||||
|
return this->resource_;
|
||||||
|
}
|
||||||
|
}
|
20
src/utils/binary_resource.hpp
Normal file
20
src/utils/binary_resource.hpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
class binary_resource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
binary_resource(int id, std::string file);
|
||||||
|
|
||||||
|
std::string get_extracted_file(bool fatal_if_overwrite_fails = false);
|
||||||
|
const std::string& get_data() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string resource_;
|
||||||
|
std::string filename_;
|
||||||
|
std::string path_;
|
||||||
|
};
|
||||||
|
}
|
@@ -71,6 +71,12 @@ namespace utils::hook
|
|||||||
return static_cast<T(*)(Args ...)>(this->get_original())(args...);
|
return static_cast<T(*)(Args ...)>(this->get_original())(args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... Args>
|
||||||
|
T invoke_pascal(Args... args)
|
||||||
|
{
|
||||||
|
return static_cast<T(__stdcall*)(Args ...)>(this->get_original())(args...);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] void* get_original() const;
|
[[nodiscard]] void* get_original() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Reference in New Issue
Block a user