Organize scripts and add TWS plugins with HTTP timeouts

Vendor pinned T5 and T6 plugin sources under the new plugins layout. Add native HTTP deadlines, status and timing diagnostics, telemetry integration, and build/test tooling.
This commit is contained in:
2026-10-09 11:02:20 -04:00
parent b172086301
commit 1121b44857
253 changed files with 28028 additions and 59 deletions
@@ -0,0 +1,354 @@
#include <stdinc.hpp>
#include "array.hpp"
#include "execution.hpp"
namespace scripting
{
array_value::array_value(const array* array, const script_value& key)
: array_(array)
, key_(key)
{
const auto value = this->array_->get(key);
this->script_value::operator=(value);
}
void array_value::operator=(const script_value& value)
{
this->array_->set(this->key_, value);
this->script_value::operator=(value);
}
array::array(const std::uint32_t id)
: id_(id)
{
this->add();
}
array::array(const array& other)
{
this->operator=(other);
}
array::array(array&& other) noexcept
{
this->id_ = other.id_;
other.id_ = 0;
}
array::array()
{
this->id_ = make_array();
}
array::~array()
{
this->release();
}
array& array::operator=(const array& other)
{
if (&other != this)
{
this->release();
this->id_ = other.id_;
this->add();
}
return *this;
}
array& array::operator=(array&& other) noexcept
{
if (&other != this)
{
this->release();
this->id_ = other.id_;
other.id_ = 0;
}
return *this;
}
void array::add() const
{
if (this->id_)
{
game::VariableValue value{};
value.u.uintValue = this->id_;
value.type = game::SCRIPT_OBJECT;
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value);
}
}
void array::release() const
{
if (this->id_)
{
game::VariableValue value{};
value.u.uintValue = this->id_;
value.type = game::SCRIPT_OBJECT;
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, value.type, value.u);
}
}
std::vector<script_value> array::get_keys() const
{
std::vector<script_value> result;
auto current = game::scr_VarGlob->objectVariableChildren[this->id_].firstChild;
while (current)
{
const auto var = game::scr_VarGlob->childVariableValue[current];
if (var.type == game::SCRIPT_NONE)
{
current = var.nextSibling;
continue;
}
const auto string_value = static_cast<std::uint8_t>(var.name_lo) + (var.k.keys.name_hi << 8);
const auto* str = game::SL_ConvertToString(string_value);
script_value key;
if (string_value < 0x40000 && str)
{
key = str;
}
else
{
key = (string_value - 0x800000) & 0xFFFFFF;
}
result.push_back(key);
current = var.nextSibling;
}
return result;
}
std::uint32_t array::size() const
{
return game::Scr_GetSelf(game::SCRIPTINSTANCE_SERVER, this->id_);
}
std::uint32_t array::push_back(const script_value& value) const
{
this->set(this->size(), value);
return this->size();
}
void array::erase(const std::uint32_t index) const
{
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, (index - 0x800000) & 0xFFFFFF);
if (variable_id)
{
game::RemoveVariableValue(game::SCRIPTINSTANCE_SERVER, this->id_, variable_id);
}
}
void array::erase(const std::string& key) const
{
const auto string_value = game::SL_GetString(key.data(), 0);
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
if (variable_id)
{
game::RemoveVariableValue(game::SCRIPTINSTANCE_SERVER, this->id_, variable_id);
}
}
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 std::string& key) const
{
const auto string_value = game::SL_GetString(key.data(), 0);
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
if (!variable_id)
{
return {};
}
const auto value = game::scr_VarGlob->childVariableValue[variable_id];
game::VariableValue variable{};
variable.u = value.u.u;
variable.type = static_cast<game::scriptType_e>(value.type);
return variable;
}
script_value array::get(const std::uint32_t index) const
{
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, (index - 0x800000) & 0xFFFFFF);
if (!variable_id)
{
return {};
}
const auto value = game::scr_VarGlob->childVariableValue[variable_id];
game::VariableValue variable{};
variable.u = value.u.u;
variable.type = static_cast<game::scriptType_e>(value.type);
return variable;
}
script_value array::get(const script_value& key) const
{
if (key.is<int>())
{
return this->get(key.as<int>());
}
if (key.is<std::string>())
{
return this->get(key.as<std::string>());
}
throw std::runtime_error(std::format("invalid key type '{}'", key.type_name()));
}
void array::set(const std::string& key, const script_value& value) const
{
const auto variable_id = this->get_value_id(key);
if (!variable_id)
{
return;
}
const auto& value_raw = value.get_raw();
const auto variable = &game::scr_VarGlob->childVariableValue[variable_id];
game::VariableValue variable_value{};
variable_value.type = variable->type;
variable_value.u = variable->u.u;
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value_raw);
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, variable_value.type, variable_value.u);
variable->type = gsl::narrow_cast<char>(value_raw.type);
variable->u.u = value_raw.u;
}
void array::set(const std::uint32_t index, const script_value& value) const
{
const auto variable_id = this->get_value_id(index);
if (!variable_id)
{
return;
}
const auto& value_raw = value.get_raw();
const auto variable = &game::scr_VarGlob->childVariableValue[variable_id];
game::VariableValue variable_value{};
variable_value.type = variable->type;
variable_value.u = variable->u.u;
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value_raw);
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, variable_value.type, variable_value.u);
variable->type = gsl::narrow_cast<char>(value_raw.type);
variable->u.u = value_raw.u;
}
void array::set(const script_value& key, const script_value& value) const
{
if (key.is<int>())
{
return this->set(key.as<int>(), value);
}
if (key.is<std::string>())
{
return this->set(key.as<std::string>(), value);
}
throw std::runtime_error(std::format("invalid key type '{}'", key.type_name()));
}
std::uint32_t array::get_entity_id() const
{
return this->id_;
}
std::uint32_t array::get_value_id(const std::string& key) const
{
const auto string_value = game::SL_GetString(key.data(), 0);
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
if (!variable_id)
{
return game::GetNewVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
}
return variable_id;
}
std::uint32_t array::get_value_id(const std::uint32_t index) const
{
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, (index - 0x800000) & 0xFFFFFF);
if (!variable_id)
{
return game::GetNewArrayVariable(game::SCRIPTINSTANCE_SERVER, this->id_, index);
}
return variable_id;
}
entity array::get_raw() const
{
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);
}
}
@@ -0,0 +1,110 @@
#pragma once
#include "script_value.hpp"
#include "container_iterator.hpp"
namespace scripting
{
class array;
class array_value : public script_value
{
public:
array_value(const array* array, const script_value& key);
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:
script_value key_;
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
{
public:
array();
array(const std::uint32_t id);
array(const array& other);
array(array&& other) noexcept;
~array();
array& operator=(const array& other);
array& operator=(array&& other) noexcept;
std::vector<script_value> get_keys() const;
std::uint32_t size() const;
std::uint32_t push_back(const script_value& value) const;
template <typename ...Args>
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 std::string&) const;
script_value get(const std::uint32_t) const;
void set(const script_value&, const script_value&) const;
void set(const std::string&, const script_value&) const;
void set(const std::uint32_t, const script_value&) const;
std::uint32_t get_entity_id() const;
std::uint32_t get_value_id(const std::string&) const;
std::uint32_t get_value_id(std::uint32_t) const;
entity get_raw() const;
array_value operator[](const script_value& key) const;
array_iterator begin() const;
array_iterator end() const;
array_iterator find(const script_value& key) const;
private:
void add() const;
void release() const;
std::uint32_t id_{};
};
}
@@ -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_;
};
}
@@ -0,0 +1,123 @@
#include <stdinc.hpp>
#include "entity.hpp"
#include "script_value.hpp"
#include "execution.hpp"
namespace scripting
{
entity::entity()
: entity(0)
{
}
entity::entity(const entity& other) : entity(other.entity_id_)
{
}
entity::entity(entity&& other) noexcept
{
this->entity_id_ = other.entity_id_;
other.entity_id_ = 0;
}
entity::entity(const unsigned int entity_id)
: entity_id_(entity_id)
{
this->add();
}
entity::~entity()
{
this->release();
}
entity& entity::operator=(const entity& other)
{
if (&other != this)
{
this->release();
this->entity_id_ = other.entity_id_;
this->add();
}
return *this;
}
entity& entity::operator=(entity&& other) noexcept
{
if (&other != this)
{
this->release();
this->entity_id_ = other.entity_id_;
other.entity_id_ = 0;
}
return *this;
}
unsigned int entity::get_entity_id() const
{
return this->entity_id_;
}
game::scr_entref_t entity::get_entity_reference() const
{
if (!this->entity_id_)
{
const auto not_null = static_cast<uint16_t>(~0ui16);
return game::scr_entref_t{not_null, not_null};
}
return game::Scr_GetEntityIdRef(this->get_entity_id());
}
bool entity::operator==(const entity& other) const noexcept
{
return this->get_entity_id() == other.get_entity_id();
}
bool entity::operator!=(const entity& other) const noexcept
{
return !this->operator==(other);
}
void entity::add() const
{
if (this->entity_id_)
{
game::VariableValue value;
value.type = game::SCRIPT_OBJECT;
value.u.uintValue = this->entity_id_;
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value);
}
}
void entity::release() const
{
if (this->entity_id_)
{
game::VariableValue value;
value.type = game::SCRIPT_OBJECT;
value.u.uintValue = this->entity_id_;
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, value.type, value.u);
}
}
void entity::set(const std::string& field, const script_value& value) const
{
set_entity_field(*this, field, value);
}
template <>
script_value entity::get<script_value>(const std::string& field) const
{
return get_entity_field(*this, field);
}
script_value entity::call(const std::string& name, const std::vector<script_value>& arguments) const
{
return call_function(name, *this, arguments);
}
}
@@ -0,0 +1,49 @@
#pragma once
#include "game/game.hpp"
#include "script_value.hpp"
namespace scripting
{
class entity final
{
public:
entity();
entity(unsigned int entity_id);
entity(const entity& other);
entity(entity&& other) noexcept;
~entity();
entity& operator=(const entity& other);
entity& operator=(entity&& other) noexcept;
void set(const std::string& field, const script_value& value) const;
template <typename T = script_value>
T get(const std::string& field) const;
script_value call(const std::string& name, const std::vector<script_value>& arguments = {}) const;
unsigned int get_entity_id() const;
game::scr_entref_t get_entity_reference() const;
bool operator ==(const entity& other) const noexcept;
bool operator !=(const entity& other) const noexcept;
private:
unsigned int entity_id_;
void add() const;
void release() const;
};
template <>
script_value entity::get(const std::string& field) const;
template <typename T>
T entity::get(const std::string& field) const
{
return this->get<script_value>(field).as<T>();
}
}
@@ -0,0 +1,13 @@
#pragma once
#include "script_value.hpp"
#include "entity.hpp"
namespace scripting
{
struct event
{
std::string name;
entity entity{};
std::vector<script_value> arguments;
};
}
@@ -0,0 +1,210 @@
#include <stdinc.hpp>
#include "execution.hpp"
#include "safe_execution.hpp"
#include "stack_isolation.hpp"
#include "component/scripting.hpp"
namespace scripting
{
namespace
{
game::VariableValue* allocate_argument()
{
game::VariableValue* value_ptr = ++game::scr_VmPub->top;
++game::scr_VmPub->inparamcount;
return value_ptr;
}
script_value get_return_value()
{
if (game::scr_VmPub->inparamcount == 0)
{
return {};
}
game::Scr_ClearOutParams(game::SCRIPTINSTANCE_SERVER);
game::scr_VmPub->outparamcount = game::scr_VmPub->inparamcount;
game::scr_VmPub->inparamcount = 0;
return script_value(game::scr_VmPub->top[1 - game::scr_VmPub->outparamcount]);
}
int get_field_id(const int classnum, const std::string& field)
{
if (scripting::fields_table[classnum].find(field) != scripting::fields_table[classnum].end())
{
return scripting::fields_table[classnum][field];
}
return -1;
}
}
void push_value(const script_value& value)
{
auto* value_ptr = allocate_argument();
*value_ptr = value.get_raw();
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, value_ptr);
}
void notify(const entity& entity, const std::string& event, const std::vector<script_value>& arguments)
{
stack_isolation _;
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
{
push_value(*i);
}
const auto event_id = game::SL_GetString(event.data(), 0);
game::Scr_NotifyId(game::SCRIPTINSTANCE_SERVER, 0, entity.get_entity_id(), event_id, game::scr_VmPub->inparamcount);
}
script_value call_function(const std::string& name, const entity& entity,
const std::vector<script_value>& arguments)
{
const auto entref = entity.get_entity_reference();
const auto function = find_function_ptr(name);
if (!function)
{
throw std::runtime_error("Unknown function '" + name + "'");
}
stack_isolation _;
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
{
push_value(*i);
}
game::scr_VmPub->outparamcount = game::scr_VmPub->inparamcount;
game::scr_VmPub->inparamcount = 0;
if (!safe_execution::call(function, entref))
{
const auto error = reinterpret_cast<const char*>(SELECT(0x2E27C70, 0x2DF7F70));
throw std::runtime_error(error);
}
return get_return_value();
}
script_value call_function(const std::string& name, const std::vector<script_value>& arguments)
{
return call_function(name, entity(), arguments);
}
template <>
script_value call(const std::string& name, const std::vector<script_value>& arguments)
{
return call_function(name, arguments);
}
script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector<script_value>& arguments)
{
const auto id = entity.get_entity_id();
stack_isolation _;
for (auto i = arguments.rbegin(); i != arguments.rend(); ++i)
{
push_value(*i);
}
game::VariableValue value{};
value.type = game::SCRIPT_OBJECT;
value.u.uintValue = id;
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value);
const auto local_id = game::AllocThread(game::SCRIPTINSTANCE_SERVER, id);
const auto result = game::VM_Execute(game::SCRIPTINSTANCE_SERVER, local_id, pos, arguments.size());
game::RemoveRefToObject(game::SCRIPTINSTANCE_SERVER, result);
return get_return_value();
}
script_value get_custom_field(const entity& entity, const std::string& field)
{
const object object = entity.get_entity_id();
return object.get(field);
}
void set_custom_field(const entity& entity, const std::string& field, const script_value& value)
{
const object object = entity.get_entity_id();
object.set(field, value);
}
void set_entity_field(const entity& entity, const std::string& field, const script_value& value)
{
const auto entref = entity.get_entity_reference();
const int id = get_field_id(entref.classnum, field);
if (id != -1)
{
stack_isolation _;
push_value(value);
game::scr_VmPub->outparamcount = game::scr_VmPub->inparamcount;
game::scr_VmPub->inparamcount = 0;
if (!safe_execution::set_entity_field(entref, id))
{
throw std::runtime_error("Failed to set value for field '" + field + "'");
}
}
else
{
set_custom_field(entity, field, value);
}
}
script_value get_entity_field(const entity& entity, const std::string& field)
{
const auto entref = entity.get_entity_reference();
const auto id = get_field_id(entref.classnum, field);
if (id != -1)
{
stack_isolation _;
game::VariableValue value{};
if (!safe_execution::get_entity_field(entref, id, &value))
{
throw std::runtime_error("Failed to get value for field '" + field + "'");
}
const auto __ = gsl::finally([&value]()
{
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, value.type, value.u);
});
return value;
}
else
{
return get_custom_field(entity, field);
}
}
unsigned int make_array()
{
unsigned int index = 0;
const auto variable = game::AllocVariable(game::SCRIPTINSTANCE_SERVER, &index);
variable->w.type = game::SCRIPT_ARRAY;
variable->u.f.prev = 0;
variable->u.f.next = 0;
return index;
}
unsigned int make_object()
{
unsigned int index = 0;
const auto variable = game::AllocVariable(game::SCRIPTINSTANCE_SERVER, &index);
variable->w.type = game::SCRIPT_STRUCT;
variable->u.f.prev = 0;
return index;
}
}
@@ -0,0 +1,39 @@
#pragma once
#include "game/game.hpp"
#include "entity.hpp"
#include "array.hpp"
#include "object.hpp"
#include "function.hpp"
#include "thread.hpp"
#include "script_value.hpp"
namespace scripting
{
void push_value(const script_value& value);
script_value call_function(const std::string& name, const std::vector<script_value>& arguments);
script_value call_function(const std::string& name, const entity& entity,
const std::vector<script_value>& arguments);
template <typename T = script_value>
T call(const std::string& name, const std::vector<script_value>& arguments = {});
template <>
script_value call(const std::string& name, const std::vector<script_value>& arguments);
template <typename T>
T call(const std::string& name, const std::vector<script_value>& arguments)
{
return call<script_value>(name, arguments).as<T>();
}
script_value exec_ent_thread(const entity& entity, const char* pos, const std::vector<script_value>& arguments);
void set_entity_field(const entity& entity, const std::string& field, const script_value& value);
script_value get_entity_field(const entity& entity, const std::string& field);
void notify(const entity& entity, const std::string& event, const std::vector<script_value>& arguments);
unsigned int make_array();
unsigned int make_object();
}
@@ -0,0 +1,41 @@
#include <stdinc.hpp>
#include "function.hpp"
#include "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
{
return scripting::find_function(this->pos_);
}
script_value function::call(const entity& self, std::vector<script_value> arguments) const
{
if (this->pos_ == 0)
{
throw std::runtime_error("function pos not set");
}
return exec_ent_thread(self, this->pos_, arguments);
}
}
@@ -0,0 +1,36 @@
#pragma once
#include "entity.hpp"
#include "script_value.hpp"
namespace scripting
{
class function
{
public:
function() = default;
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_;
};
}
@@ -0,0 +1,270 @@
#include <stdinc.hpp>
#include "object.hpp"
#include "execution.hpp"
namespace scripting
{
object_value::object_value(const object* object, const std::string& key)
: object_(object)
, key_(key)
{
const auto value = this->object_->get(key);
this->script_value::operator=(value);
}
void object_value::operator=(const script_value& value)
{
this->object_->set(this->key_, value);
this->script_value::operator=(value);
}
object::object(const std::uint32_t id)
: id_(id)
{
this->add();
}
object::object(const object& other)
{
this->operator=(other);
}
object::object(object&& other) noexcept
{
this->id_ = other.id_;
other.id_ = 0;
}
object::object()
{
this->id_ = make_object();
}
object::~object()
{
this->release();
}
object& object::operator=(const object& other)
{
if (&other != this)
{
this->release();
this->id_ = other.id_;
this->add();
}
return *this;
}
object& object::operator=(object&& other) noexcept
{
if (&other != this)
{
this->release();
this->id_ = other.id_;
other.id_ = 0;
}
return *this;
}
void object::add() const
{
if (this->id_)
{
game::VariableValue value{};
value.u.uintValue = this->id_;
value.type = game::SCRIPT_OBJECT;
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value);
}
}
void object::release() const
{
if (this->id_)
{
game::VariableValue value{};
value.u.uintValue = this->id_;
value.type = game::SCRIPT_OBJECT;
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, value.type, value.u);
}
}
void object::iterate_keys(const std::function<bool(const std::string& key)>& callback) const
{
auto current = game::scr_VarGlob->objectVariableChildren[this->id_].firstChild;
while (current)
{
const auto var = game::scr_VarGlob->childVariableValue[current];
const auto string_id = static_cast<std::uint8_t>(var.name_lo) + (var.k.keys.name_hi << 8);
// pluto has this table relocated
static const auto max_string_id = *reinterpret_cast<int*>(SELECT(0x6927A1 + 2, 0x51A9A1 + 2));
if (string_id < max_string_id)
{
static const auto address = *reinterpret_cast<unsigned int*>(SELECT(0x635D7E + 3, 0x5466C5 + 3));
const auto string = reinterpret_cast<const char**>(address)[string_id];
if (string != nullptr && callback(string))
{
return;
}
}
current = var.nextSibling;
}
}
std::vector<std::string> object::get_keys() const
{
std::vector<std::string> result;
iterate_keys([&](const std::string& key)
{
result.emplace_back(key);
return false;
});
return result;
}
std::optional<std::string> object::get_next_key(const std::string& current) const
{
auto is_next = false;
std::optional<std::string> next;
iterate_keys([&](const std::string& key)
{
if (is_next)
{
next.emplace(key);
return true;
}
if (key == current)
{
is_next = true;
}
return false;
});
return next;
}
std::uint32_t object::size() const
{
return game::Scr_GetSelf(game::SCRIPTINSTANCE_SERVER, this->id_);
}
void object::erase(const std::string& key) const
{
const auto string_value = game::SL_GetCanonicalString(key.data(), 0);
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
if (variable_id)
{
game::RemoveVariableValue(game::SCRIPTINSTANCE_SERVER, this->id_, variable_id);
}
}
void object::erase(const object_iterator& iter) const
{
this->erase(iter->first);
}
script_value object::get(const std::string& key) const
{
const auto string_value = game::SL_GetCanonicalString(key.data(), 0);
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
if (!variable_id)
{
return {};
}
const auto value = game::scr_VarGlob->childVariableValue[variable_id];
game::VariableValue variable{};
variable.u = value.u.u;
variable.type = static_cast<game::scriptType_e>(value.type);
return variable;
}
void object::set(const std::string& key, const script_value& value) const
{
const auto& value_raw = value.get_raw();
const auto variable_id = this->get_value_id(key);
if (!variable_id)
{
return;
}
const auto variable = &game::scr_VarGlob->childVariableValue[variable_id];
game::VariableValue variable_{};
variable_.type = variable->type;
variable_.u = variable->u.u;
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &value_raw);
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, variable_.type, variable_.u);
variable->type = gsl::narrow_cast<char>(value_raw.type);
variable->u.u = value_raw.u;
}
std::uint32_t object::get_entity_id() const
{
return this->id_;
}
std::uint32_t object::get_value_id(const std::string& key) const
{
const auto string_value = game::SL_GetCanonicalString(key.data(), 0);
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
if (!variable_id)
{
return game::GetNewVariable(game::SCRIPTINSTANCE_SERVER, this->id_, string_value);
}
return variable_id;
}
entity object::get_raw() const
{
return entity(this->id_);
}
object_value object::operator[](const std::string& key) const
{
return object_value(this, key);
}
object_iterator object::begin() const
{
const auto keys = this->get_keys();
return object_iterator(this, keys, 0);
}
object_iterator object::end() const
{
return object_iterator(this);
}
object_iterator object::find(const std::string& key) const
{
const auto keys = this->get_keys();
for (auto i = 0u; i < keys.size(); i++)
{
if (keys[i] == key)
{
return object_iterator(this, keys, i);
}
}
return object_iterator(this);
}
}
@@ -0,0 +1,95 @@
#pragma once
#include "script_value.hpp"
#include "container_iterator.hpp"
namespace scripting
{
class object;
class object_value : public script_value
{
public:
object_value(const object* object, const std::string& key);
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("object field '{}' {}", this->key_, e.what()));
}
}
private:
const object* object_;
std::string key_;
};
template <typename IteratorType>
class object_iterator_base : public IteratorType
{
public:
object_iterator_base(const object* container)
: IteratorType(container)
{
}
object_iterator_base(const object* container, const std::vector<std::string>& keys, const std::int64_t key_index)
: IteratorType(container, keys, key_index)
{
}
};
using object_iterator = object_iterator_base<container_iterator<object, std::string, object_value>>;
class object final
{
public:
object();
object(const std::uint32_t id);
object(const object& other);
object(object&& other) noexcept;
~object();
object& operator=(const object& other);
object& operator=(object&& other) noexcept;
void iterate_keys(const std::function<bool(const std::string& key)>& callback) const;
std::vector<std::string> get_keys() const;
std::optional<std::string> get_next_key(const std::string& current) const;
std::uint32_t size() const;
void erase(const std::string& key) const;
void erase(const object_iterator& iter) const;
script_value get(const std::string&) const;
void set(const std::string&, const script_value&) const;
std::uint32_t get_entity_id() const;
std::uint32_t get_value_id(const std::string&) const;
entity get_raw() const;
object_value operator[](const std::string& key) const;
object_iterator begin() const;
object_iterator end() const;
object_iterator find(const std::string& key) const;
private:
void add() const;
void release() const;
std::uint32_t id_{};
};
}
@@ -0,0 +1,67 @@
#include <stdinc.hpp>
#include "safe_execution.hpp"
#pragma warning(push)
#pragma warning(disable: 4611)
namespace scripting::safe_execution
{
bool execute_with_seh(const script_function function, const game::scr_entref_t& entref)
{
__try
{
function(entref);
return true;
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return false;
}
}
bool call(const script_function function, const game::scr_entref_t& entref)
{
*game::g_script_error_level += 1;
if (game::_setjmp(&game::g_script_error[*game::g_script_error_level], 0, 0, 0))
{
*game::g_script_error_level -= 1;
return false;
}
const auto result = execute_with_seh(function, entref);
*game::g_script_error_level -= 1;
return result;
}
bool set_entity_field(const game::scr_entref_t& entref, const int offset)
{
*game::g_script_error_level += 1;
if (game::_setjmp(&game::g_script_error[*game::g_script_error_level], 0, 0, 0))
{
*game::g_script_error_level -= 1;
return false;
}
game::Scr_SetObjectField(entref.classnum, entref.entnum, offset);
*game::g_script_error_level -= 1;
return true;
}
bool get_entity_field(const game::scr_entref_t& entref, const int offset, game::VariableValue* value)
{
*game::g_script_error_level += 1;
if (game::_setjmp(&game::g_script_error[*game::g_script_error_level], 0, 0, 0))
{
value->type = game::SCRIPT_NONE;
value->u.intValue = 0;
*game::g_script_error_level -= 1;
return false;
}
*value = game::GetEntityFieldValue(game::SCRIPTINSTANCE_SERVER, entref.classnum, entref.entnum, 0, offset);
*game::g_script_error_level -= 1;
return true;
}
}
@@ -0,0 +1,14 @@
#pragma once
#include "game/game.hpp"
namespace scripting::safe_execution
{
using script_function = void(*)(game::scr_entref_t);
bool execute_with_seh(const script_function function, const game::scr_entref_t& entref);
bool call(script_function function, const game::scr_entref_t& entref);
bool set_entity_field(const game::scr_entref_t& entref, int offset);
bool get_entity_field(const game::scr_entref_t& entref, int offset, game::VariableValue* value);
}
@@ -0,0 +1,478 @@
#include <stdinc.hpp>
#include "script_value.hpp"
#include "entity.hpp"
#include "array.hpp"
#include "function.hpp"
#include "object.hpp"
namespace scripting
{
/***********************************************
* Constructors
**********************************************/
script_value::script_value(const game::VariableValue& value)
: value_(value)
{
}
script_value::script_value(void* value)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_INTEGER;
variable.u.intValue = reinterpret_cast<uintptr_t>(value);
this->value_ = variable;
}
script_value::script_value(const int value)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_INTEGER;
variable.u.intValue = value;
this->value_ = variable;
}
script_value::script_value(const unsigned int value)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_INTEGER;
variable.u.uintValue = value;
this->value_ = variable;
}
script_value::script_value(const bool value)
: script_value(static_cast<unsigned>(value))
{
}
script_value::script_value(const float value)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_FLOAT;
variable.u.floatValue = value;
this->value_ = variable;
}
script_value::script_value(const double value)
: script_value(static_cast<float>(value))
{
}
script_value::script_value(const char* value)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_STRING;
variable.u.stringValue = game::SL_GetString(value, 0);
const auto _ = gsl::finally([&variable]()
{
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, variable.type, variable.u);
});
this->value_ = variable;
}
script_value::script_value(const std::string& value)
: script_value(value.data())
{
}
script_value::script_value(const entity& value)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_OBJECT;
variable.u.pointerValue = value.get_entity_id();
this->value_ = variable;
}
script_value::script_value(const array& value)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_OBJECT;
variable.u.pointerValue = value.get_entity_id();
this->value_ = variable;
}
script_value::script_value(const object& value)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_OBJECT;
variable.u.pointerValue = value.get_entity_id();
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)
{
game::VariableValue variable{};
variable.type = game::SCRIPT_VECTOR;
variable.u.vectorValue = game::Scr_AllocVector(game::SCRIPTINSTANCE_SERVER, value);
const auto _ = gsl::finally([&variable]()
{
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, variable.type, variable.u);
});
this->value_ = variable;
}
/***********************************************
* Integer
**********************************************/
template <>
bool script_value::is<int>() const
{
return this->get_raw().type == game::SCRIPT_INTEGER;
}
template <>
bool script_value::is<unsigned int>() const
{
return this->is<int>();
}
template <>
bool script_value::is<unsigned short>() const
{
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 <>
bool script_value::is<bool>() const
{
return this->is<int>();
}
template <>
int script_value::get() const
{
return this->get_raw().u.intValue;
}
template <>
unsigned int script_value::get() const
{
return this->get_raw().u.uintValue;
}
template <>
bool script_value::get() const
{
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
**********************************************/
template <>
bool script_value::is<float>() const
{
const auto type = this->get_raw().type;
return type == game::SCRIPT_FLOAT || type == game::SCRIPT_INTEGER;
}
template <>
bool script_value::is<double>() const
{
return this->is<float>();
}
template <>
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;
}
template <>
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);
}
/***********************************************
* String
**********************************************/
template <>
bool script_value::is<const char*>() const
{
return this->get_raw().type == game::SCRIPT_STRING
|| this->get_raw().type == game::SCRIPT_ISTRING;
}
template <>
bool script_value::is<std::string>() const
{
return this->is<const char*>();
}
template <>
const char* script_value::get() const
{
return game::SL_ConvertToString(static_cast<unsigned int>(this->get_raw().u.stringValue));
}
template <>
std::string script_value::get() const
{
return this->get<const char*>();
}
/***********************************************
* Entity
**********************************************/
template <>
bool script_value::is<entity>() const
{
return this->get_raw().type == game::SCRIPT_OBJECT;
}
template <>
entity script_value::get() const
{
return entity(this->get_raw().u.pointerValue);
}
/***********************************************
* Array
**********************************************/
template <>
bool script_value::is<array>() const
{
if (this->get_raw().type != game::SCRIPT_OBJECT)
{
return false;
}
const auto id = this->get_raw().u.uintValue;
const auto type = game::scr_VarGlob->objectVariableValue[id].w.type & 0x7F;
return type == game::SCRIPT_ARRAY;
}
template <>
array script_value::get() const
{
return array(this->get_raw().u.uintValue);
}
/***********************************************
* Struct
**********************************************/
template <>
bool script_value::is<object>() const
{
if (this->get_raw().type != game::SCRIPT_OBJECT)
{
return false;
}
const auto id = this->get_raw().u.uintValue;
const auto type = game::scr_VarGlob->objectVariableValue[id].w.type & 0x7F;
return type == game::SCRIPT_STRUCT || type == game::SCRIPT_ENTITY;
}
template <>
object script_value::get() const
{
return object(this->get_raw().u.uintValue);
}
/***********************************************
* Function
**********************************************/
template <>
bool script_value::is<function>() const
{
return this->get_raw().type == game::SCRIPT_FUNCTION;
}
template <>
function script_value::get() const
{
return function(this->get_raw().u.codePosValue);
}
/***********************************************
* Vector
**********************************************/
template <>
bool script_value::is<vector>() const
{
return this->get_raw().type == game::SCRIPT_VECTOR;
}
template <>
bool script_value::is<float*>() const
{
return this->is<vector>();
}
template <>
vector script_value::get() const
{
return this->get_raw().u.vectorValue;
}
/***********************************************
*
**********************************************/
const game::VariableValue& script_value::get_raw() const
{
return this->value_.get();
}
std::string script_value::to_string() const
{
if (this->is<int>())
{
return utils::string::va("%i", this->as<int>());
}
if (this->is<float>())
{
return utils::string::va("%f", this->as<float>());
}
if (this->is<std::string>())
{
return this->as<std::string>();
}
if (this->is<vector>())
{
const auto vec = this->as<vector>();
return utils::string::va("(%g, %g, %g)",
vec.get_x(),
vec.get_y(),
vec.get_z()
);
}
if (this->is<function>())
{
const auto func = this->as<function>();
const auto& name = func.get_name();
return utils::string::va("[[ %s ]]", name.data());
}
return this->type_name();
}
function_argument::function_argument(const arguments& args, const script_value& value, const size_t index, const bool exists)
: script_value(value)
, values_(args)
, index_(index)
, exists_(exists)
{
}
function_arguments::function_arguments(const arguments& values)
: values_(values)
{
}
variadic_args::variadic_args(const size_t begin)
: std::vector<function_argument>({})
, begin_(begin)
{
}
function_argument variadic_args::operator[](size_t index) const
{
if (index >= this->size())
{
throw std::runtime_error(utils::string::va("parameter %d does not exist", this->begin_ + 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();
}
}
@@ -0,0 +1,393 @@
#pragma once
#include "game/game.hpp"
#include "variable_value.hpp"
#include "vector.hpp"
#include <utils/string.hpp>
namespace scripting
{
class entity;
class array;
class object;
class function;
class script_value;
namespace
{
std::unordered_map<int, std::string> typenames =
{
{0, "undefined"},
{1, "object"},
{2, "string"},
{3, "localized string"},
{4, "vector"},
{5, "hash"},
{6, "float"},
{7, "int"},
{8, "codepos"},
{9, "precodepos"},
{10, "function"},
{11, "stack"},
{12, "animation"},
{13, "developer codepos"},
{14, "thread"},
{15, "thread"},
{16, "thread"},
{17, "thread"},
{18, "struct"},
{19, "removed entity"},
{20, "entity"},
{21, "array"},
{22, "removed thread"},
{23, "<free>"},
{24, "thread list"},
{25, "ent 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 & 0x7F;
return typenames[type];
}
else
{
return typenames[value.type];
}
}
template <typename T>
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(object))
{
return "struct";
}
if (info == typeid(function))
{
return "function";
}
if (info == typeid(vector))
{
return "vector";
}
return info.name();
}
}
using arguments = std::vector<script_value>;
class script_value
{
public:
script_value() = default;
script_value(const game::VariableValue& value);
script_value(void* value);
script_value(int value);
script_value(unsigned int value);
script_value(bool value);
script_value(float value);
script_value(double value);
script_value(const char* value);
script_value(const std::string& value);
script_value(const entity& value);
script_value(const array& value);
script_value(const object& value);
script_value(const function& value);
script_value(const vector& value);
private:
template <typename T>
T get() const
{
if constexpr (std::is_pointer<T>::value)
{
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");
}
public:
#define ADD_TYPE(type) \
template <> \
bool is<type>() const; \
private: \
template <> \
type get<>() const; \
public: \
template <typename T>
bool is() const
{
if (std::is_pointer<T>::value && this->is<unsigned int>())
{
return true;
}
if (std::is_constructible<T, std::string>::value && this->is<std::string>())
{
return true;
}
return false;
}
ADD_TYPE(bool)
ADD_TYPE(int)
ADD_TYPE(unsigned int)
ADD_TYPE(unsigned short)
ADD_TYPE(float)
ADD_TYPE(float*)
ADD_TYPE(double)
ADD_TYPE(const char*)
ADD_TYPE(std::string)
ADD_TYPE(vector)
ADD_TYPE(array)
ADD_TYPE(object)
ADD_TYPE(function)
ADD_TYPE(entity)
ADD_TYPE(script_value)
template <typename T>
T as() const
{
if (!this->is<T>())
{
const auto type = get_typename(this->get_raw());
const auto c_type = get_c_typename<T>();
throw std::runtime_error(utils::string::va("has type '%s' but should be '%s'", type.data(), c_type.data()));
}
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
{
return get_typename(this->get_raw());
}
template <template<class, class> class C, class T, typename ArrayType = array>
script_value(const C<T, std::allocator<T>>& container)
{
ArrayType array_{};
for (const auto& value : container)
{
array_.emplace_back(value);
}
game::VariableValue value{};
value.type = game::SCRIPT_OBJECT;
value.u.pointerValue = array_.get_entity_id();
this->value_ = value;
}
template<class ...T>
arguments operator()(T... arguments) const
{
return this->as<function>().call({arguments...});
}
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;
variable_value value_{};
};
class function_argument;
class variadic_args : public std::vector<function_argument>
{
public:
variadic_args(const size_t begin);
function_argument operator[](size_t index) const;
private:
size_t begin_;
};
class function_argument : public script_value
{
public:
function_argument(const arguments& args, const script_value& value, const size_t index, const bool exists);
template <typename T>
T as() const
{
if (!this->exists_)
{
throw std::runtime_error(utils::string::va("parameter %d does not exist", this->index_));
}
try
{
return script_value::as<T>();
}
catch (const std::exception& e)
{
throw std::runtime_error(utils::string::va("parameter %d %s",
this->index_, e.what()));
}
}
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 <>
variadic_args as() const
{
variadic_args args{this->index_};
for (auto i = this->index_; i < this->values_.size(); i++)
{
args.emplace_back(this->values_, this->values_[i], i, true);
}
return args;
}
operator variadic_args() const
{
return this->as<variadic_args>();
}
template <template<class, class> class C, class T, class ArrayType = array>
operator C<T, std::allocator<T>>() const
{
const auto container_type = get_c_typename<C<T, std::allocator<T>>>();
if (!script_value::is<ArrayType>())
{
const auto type = get_typename(this->get_raw());
throw std::runtime_error(utils::string::va("has type '%s' but should be '%s'",
type.data(),
container_type.data()
));
}
C<T, std::allocator<T>> container{};
const auto array = script_value::as<ArrayType>();
for (auto i = 0u; i < array.size(); i++)
{
try
{
container.emplace_back(array.get(i).as<T>());
}
catch (const std::exception& e)
{
throw std::runtime_error(utils::string::va("element %d of parameter %d of type '%s' %s",
i, this->index_, container_type.data(), e.what()));
}
}
return container;
}
template <typename T>
operator T() const
{
return this->as<T>();
}
private:
arguments values_{};
size_t index_{};
bool exists_{};
};
class function_arguments
{
public:
function_arguments(const arguments& values);
function_argument operator[](const size_t index) const;
arguments get_raw() const;
size_t size() const;
private:
arguments values_{};
};
}
@@ -0,0 +1,27 @@
#include <stdinc.hpp>
#include "stack_isolation.hpp"
namespace scripting
{
stack_isolation::stack_isolation()
{
this->in_param_count_ = game::scr_VmPub->inparamcount;
this->out_param_count_ = game::scr_VmPub->outparamcount;
this->top_ = game::scr_VmPub->top;
this->max_stack_ = game::scr_VmPub->maxstack;
game::scr_VmPub->top = this->stack_;
game::scr_VmPub->maxstack = &this->stack_[ARRAYSIZE(this->stack_) - 1];
game::scr_VmPub->inparamcount = 0;
game::scr_VmPub->outparamcount = 0;
}
stack_isolation::~stack_isolation()
{
game::Scr_ClearOutParams(game::SCRIPTINSTANCE_SERVER);
game::scr_VmPub->inparamcount = this->in_param_count_;
game::scr_VmPub->outparamcount = this->out_param_count_;
game::scr_VmPub->top = this->top_;
game::scr_VmPub->maxstack = this->max_stack_;
}
}
@@ -0,0 +1,25 @@
#pragma once
#include "game/game.hpp"
namespace scripting
{
class stack_isolation final
{
public:
stack_isolation();
~stack_isolation();
stack_isolation(stack_isolation&&) = delete;
stack_isolation(const stack_isolation&) = delete;
stack_isolation& operator=(stack_isolation&&) = delete;
stack_isolation& operator=(const stack_isolation&) = delete;
private:
game::VariableValue stack_[512]{};
game::VariableValue* max_stack_;
game::VariableValue* top_;
unsigned int in_param_count_;
unsigned int out_param_count_;
};
}
@@ -0,0 +1,141 @@
#include <stdinc.hpp>
#include "thread.hpp"
#include "execution.hpp"
#include "../../component/scripting.hpp"
namespace scripting
{
thread::thread(unsigned int id)
: id_(id)
, type_(game::scr_VarGlob->objectVariableValue[id].w.type & 0x7F)
{
}
script_value thread::get_raw() const
{
game::VariableValue value;
value.type = game::SCRIPT_OBJECT;
value.u.uintValue = this->id_;
return value;
}
unsigned int thread::get_entity_id() const
{
return this->id_;
}
unsigned int thread::get_type() const
{
return this->type_;
}
unsigned int thread::get_wait_time() const
{
return game::scr_VarGlob->objectVariableValue[this->id_].w.waitTime >> 8;
}
unsigned int thread::get_notify_name_id() const
{
return game::scr_VarGlob->objectVariableValue[this->id_].w.notifyName >> 8;
}
unsigned int thread::get_self() const
{
return game::Scr_GetSelf(game::SCRIPTINSTANCE_SERVER, this->id_);
}
std::string thread::get_notify_name() const
{
return game::SL_ConvertToString(this->get_notify_name_id());
}
const char* thread::get_pos() const
{
const auto start_local_id = game::GetStartLocalId(game::SCRIPTINSTANCE_SERVER, this->id_);
if (this->type_ == game::SCRIPT_THREAD)
{
for (auto frame = game::scr_VmPub->function_frame; frame != game::scr_VmPub->function_frame_start; --frame)
{
if (frame->fs.localId == this->id_)
{
return frame->fs.pos;
}
}
}
unsigned int stack_id = 0;
if (this->type_ == game::SCRIPT_TIME_THREAD)
{
const auto waittime = this->get_wait_time();
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, game::scr_VarPub->timeArrayId, waittime);
const auto object_id = game::scr_VarGlob->childVariableValue[variable_id].u.f.prev;
stack_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, object_id, start_local_id + 0x10000);
}
if (this->type_ == game::SCRIPT_NOTIFY_THREAD)
{
const auto notify_name = this->get_notify_name_id();
if (!notify_name)
{
stack_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, start_local_id, 0x16001);
}
else
{
const auto self = game::Scr_GetSelf(game::SCRIPTINSTANCE_SERVER, start_local_id);
const auto variable_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, game::scr_VarPub->pauseArrayId, self + 0x10000);
const auto object_id = game::scr_VarGlob->childVariableValue[variable_id].u.f.prev;
const auto variable_id2 = game::FindVariable(game::SCRIPTINSTANCE_SERVER, object_id, start_local_id + 0x10000);
const auto variable = &game::scr_VarGlob->childVariableValue[variable_id2];
const auto variable_id3 = game::FindVariable(game::SCRIPTINSTANCE_SERVER, variable->u.u.uintValue, 0x18000);
const auto object_id2 = game::scr_VarGlob->childVariableValue[variable_id3].u.f.prev;
const auto variable_id4 = game::FindVariable(game::SCRIPTINSTANCE_SERVER, object_id2, notify_name);
const auto object_id3 = game::scr_VarGlob->childVariableValue[variable_id4].u.f.prev;
stack_id = game::FindVariable(game::SCRIPTINSTANCE_SERVER, object_id3, start_local_id + 0x10000);
}
}
const auto stack = &game::scr_VarGlob->childVariableValue[stack_id];
if (stack && stack->type == 11)
{
return stack->u.u.stackValue->pos;
}
return 0;
}
const char* thread::get_start_pos() const
{
const auto current = this->get_pos();
return scripting::find_function_start(current);
}
void thread::kill() const
{
const auto start_local_id = game::GetStartLocalId(game::SCRIPTINSTANCE_SERVER, this->id_);
if (this->type_ == game::SCRIPT_THREAD)
{
game::Scr_TerminateRunningThread(game::SCRIPTINSTANCE_SERVER, this->id_);
}
if (this->type_ == game::SCRIPT_TIME_THREAD)
{
game::Scr_TerminateWaitThread(game::SCRIPTINSTANCE_SERVER, this->id_, start_local_id);
}
if (this->type_ == game::SCRIPT_NOTIFY_THREAD)
{
game::Scr_TerminateWaittillThread(game::SCRIPTINSTANCE_SERVER, this->id_, start_local_id);
}
}
}
@@ -0,0 +1,32 @@
#pragma once
#include "entity.hpp"
#include "script_value.hpp"
namespace scripting
{
class thread
{
public:
thread(unsigned int);
script_value get_raw() const;
unsigned int get_entity_id() const;
unsigned int get_type() const;
unsigned int get_self() const;
unsigned int get_wait_time() const;
unsigned int get_notify_name_id() const;
std::string get_notify_name() const;
const char* get_pos() const;
const char* get_start_pos() const;
void kill() const;
private:
unsigned int id_{};
unsigned int type_{};
};
}
@@ -0,0 +1,68 @@
#include <stdinc.hpp>
#include "variable_value.hpp"
namespace scripting
{
variable_value::variable_value(const game::VariableValue& value)
{
this->assign(value);
}
variable_value::variable_value(const variable_value& other) noexcept
{
this->operator=(other);
}
variable_value::variable_value(variable_value&& other) noexcept
{
this->operator=(std::move(other));
}
variable_value& variable_value::operator=(const variable_value& other) noexcept
{
if (this != &other)
{
this->release();
this->assign(other.value_);
}
return *this;
}
variable_value& variable_value::operator=(variable_value&& other) noexcept
{
if (this != &other)
{
this->release();
this->value_ = other.value_;
other.value_.type = game::SCRIPT_NONE;
}
return *this;
}
variable_value::~variable_value()
{
this->release();
}
const game::VariableValue& variable_value::get() const
{
return this->value_;
}
void variable_value::assign(const game::VariableValue& value)
{
this->value_ = value;
game::AddRefToValue(game::SCRIPTINSTANCE_SERVER, &this->value_);
}
void variable_value::release()
{
if (this->value_.type != game::SCRIPT_NONE)
{
game::RemoveRefToValue(game::SCRIPTINSTANCE_SERVER, this->value_.type, this->value_.u);
this->value_.type = game::SCRIPT_NONE;
}
}
}
@@ -0,0 +1,27 @@
#pragma once
#include "game/game.hpp"
namespace scripting
{
class variable_value
{
public:
variable_value() = default;
variable_value(const game::VariableValue& value);
variable_value(const variable_value& other) noexcept;
variable_value(variable_value&& other) noexcept;
variable_value& operator=(const variable_value& other) noexcept;
variable_value& operator=(variable_value&& other) noexcept;
~variable_value();
const game::VariableValue& get() const;
private:
void assign(const game::VariableValue& value);
void release();
game::VariableValue value_{{0}, game::SCRIPT_NONE};
};
}
@@ -0,0 +1,85 @@
#include <stdinc.hpp>
#include "vector.hpp"
namespace scripting
{
vector::vector(const float* value)
{
for (auto i = 0; i < 3; ++i)
{
this->value_[i] = value[i];
}
}
vector::vector(const game::vec3_t& value)
: vector(&value[0])
{
}
vector::vector(const float x, const float y, const float z)
{
this->value_[0] = x;
this->value_[1] = y;
this->value_[2] = z;
}
vector::operator game::vec3_t& ()
{
return this->value_;
}
vector::operator const game::vec3_t& () const
{
return this->value_;
}
game::vec_t& vector::operator[](const size_t i)
{
if (i >= 3)
{
throw std::runtime_error("Out of bounds.");
}
return this->value_[i];
}
const game::vec_t& vector::operator[](const size_t i) const
{
if (i >= 3)
{
throw std::runtime_error("Out of bounds.");
}
return this->value_[i];
}
float vector::get_x() const
{
return this->operator[](0);
}
float vector::get_y() const
{
return this->operator[](1);
}
float vector::get_z() const
{
return this->operator[](2);
}
void vector::set_x(const float value)
{
this->operator[](0) = value;
}
void vector::set_y(const float value)
{
this->operator[](1) = value;
}
void vector::set_z(const float value)
{
this->operator[](2) = value;
}
}
@@ -0,0 +1,31 @@
#pragma once
#include "game/game.hpp"
namespace scripting
{
class vector final
{
public:
vector() = default;
vector(const float* value);
vector(const game::vec3_t& value);
vector(float x, float y, float z);
operator game::vec3_t& ();
operator const game::vec3_t& () const;
game::vec_t& operator[](size_t i);
const game::vec_t& operator[](size_t i) const;
float get_x() const;
float get_y() const;
float get_z() const;
void set_x(float value);
void set_y(float value);
void set_z(float value);
private:
game::vec3_t value_{ 0 };
};
}