Cleanup scripting environment
This commit is contained in:
parent
0b526b582f
commit
b106d2386d
33
src/game/scripting/context.cpp
Normal file
33
src/game/scripting/context.cpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#include "std_include.hpp"
|
||||||
|
#include "context_initializer.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
context::context() : executer_(this), parameters_(this), event_handler_(this)
|
||||||
|
{
|
||||||
|
context_initializer::initialize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
executer* context::get_executer()
|
||||||
|
{
|
||||||
|
return &this->executer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters* context::get_parameters()
|
||||||
|
{
|
||||||
|
return &this->parameters_;
|
||||||
|
}
|
||||||
|
|
||||||
|
event_handler* context::get_event_handler()
|
||||||
|
{
|
||||||
|
return &this->event_handler_;
|
||||||
|
}
|
||||||
|
|
||||||
|
chaiscript::ChaiScript* context::get_chai()
|
||||||
|
{
|
||||||
|
return &this->chai_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
src/game/scripting/context.hpp
Normal file
27
src/game/scripting/context.hpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "executer.hpp"
|
||||||
|
#include "parameters.hpp"
|
||||||
|
#include "event_handler.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
class context final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
context();
|
||||||
|
|
||||||
|
executer* get_executer();
|
||||||
|
parameters* get_parameters();
|
||||||
|
event_handler* get_event_handler();
|
||||||
|
chaiscript::ChaiScript* get_chai();
|
||||||
|
|
||||||
|
private:
|
||||||
|
executer executer_;
|
||||||
|
parameters parameters_;
|
||||||
|
event_handler event_handler_;
|
||||||
|
chaiscript::ChaiScript chai_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
246
src/game/scripting/context_initializer.cpp
Normal file
246
src/game/scripting/context_initializer.cpp
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
#include "std_include.hpp"
|
||||||
|
#include "context_initializer.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
namespace context_initializer
|
||||||
|
{
|
||||||
|
void initialize_entity(context* context)
|
||||||
|
{
|
||||||
|
const auto chai = context->get_chai();
|
||||||
|
|
||||||
|
chai->add(chaiscript::user_type<entity>(), "entity");
|
||||||
|
chai->add(chaiscript::constructor<entity()>(), "entity");
|
||||||
|
chai->add(chaiscript::constructor<entity(const entity&)>(), "entity");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(&entity::get), "get");
|
||||||
|
chai->add(chaiscript::fun(&entity::set), "set");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(&entity::on_notify), "onNotify");
|
||||||
|
chai->add(chaiscript::fun([](const entity& ent, const std::string& event,
|
||||||
|
const std::function<void(
|
||||||
|
const std::vector<chaiscript::Boxed_Value>&)>&
|
||||||
|
callback)
|
||||||
|
{
|
||||||
|
return ent.on_notify(event, callback, false);
|
||||||
|
}), "onNotify");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun([](entity& lhs, const entity& rhs) -> entity&
|
||||||
|
{
|
||||||
|
return lhs = rhs;
|
||||||
|
}), "=");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun([context](const std::string& event,
|
||||||
|
const std::function<void(const entity&,
|
||||||
|
const std::vector<chaiscript::
|
||||||
|
Boxed_Value>&)>&
|
||||||
|
callback)
|
||||||
|
{
|
||||||
|
generic_event_listener listener;
|
||||||
|
listener.event = event;
|
||||||
|
listener.is_volatile = false;
|
||||||
|
listener.callback = callback;
|
||||||
|
|
||||||
|
context->get_event_handler()->add_event_listener(listener);
|
||||||
|
}), "onNotify");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun([context](const std::string& event,
|
||||||
|
const std::function<void(const entity&,
|
||||||
|
const std::vector<chaiscript::
|
||||||
|
Boxed_Value>&)>&
|
||||||
|
callback, const bool is_volatile)
|
||||||
|
{
|
||||||
|
generic_event_listener listener;
|
||||||
|
listener.event = event;
|
||||||
|
listener.is_volatile = is_volatile;
|
||||||
|
listener.callback = callback;
|
||||||
|
|
||||||
|
context->get_event_handler()->add_event_listener(listener);
|
||||||
|
}), "onNotify");
|
||||||
|
|
||||||
|
// Notification
|
||||||
|
chai->add(chaiscript::fun(&entity::notify), "vectorNotify");
|
||||||
|
chai->add(chaiscript::fun([](const entity& ent, const std::string& event)
|
||||||
|
{
|
||||||
|
return ent.notify(event, {});
|
||||||
|
}), "notify");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[](const entity& ent, const std::string& event,
|
||||||
|
const chaiscript::Boxed_Value& a1)
|
||||||
|
{
|
||||||
|
return ent.notify(event, {a1});
|
||||||
|
}), "notify");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[](const entity& ent, const std::string& event,
|
||||||
|
const chaiscript::Boxed_Value& a1,
|
||||||
|
const chaiscript::Boxed_Value& a2)
|
||||||
|
{
|
||||||
|
return ent.notify(event, {a1, a2});
|
||||||
|
}), "notify");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[](const entity& ent, const std::string& event,
|
||||||
|
const chaiscript::Boxed_Value& a1,
|
||||||
|
const chaiscript::Boxed_Value& a2,
|
||||||
|
const chaiscript::Boxed_Value& a3)
|
||||||
|
{
|
||||||
|
return ent.notify(event, {a1, a2, a3});
|
||||||
|
}), "notify");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[](const entity& ent, const std::string& event,
|
||||||
|
const chaiscript::Boxed_Value& a1,
|
||||||
|
const chaiscript::Boxed_Value& a2,
|
||||||
|
const chaiscript::Boxed_Value& a3,
|
||||||
|
const chaiscript::Boxed_Value& a4)
|
||||||
|
{
|
||||||
|
return ent.notify(event, {a1, a2, a3, a4});
|
||||||
|
}), "notify");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[](const entity& ent, const std::string& event,
|
||||||
|
const chaiscript::Boxed_Value& a1,
|
||||||
|
const chaiscript::Boxed_Value& a2,
|
||||||
|
const chaiscript::Boxed_Value& a3,
|
||||||
|
const chaiscript::Boxed_Value& a4,
|
||||||
|
const chaiscript::Boxed_Value& a5)
|
||||||
|
{
|
||||||
|
return ent.notify(event, {a1, a2, a3, a4, a5});
|
||||||
|
}), "notify");
|
||||||
|
|
||||||
|
// Instance call
|
||||||
|
chai->add(chaiscript::fun(&entity::call), "vectorCall");
|
||||||
|
chai->add(chaiscript::fun([](const entity& ent, const std::string& function)
|
||||||
|
{
|
||||||
|
return ent.call(function, {});
|
||||||
|
}), "call");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[](const entity& ent, const std::string& function,
|
||||||
|
const chaiscript::Boxed_Value& a1)
|
||||||
|
{
|
||||||
|
return ent.call(function, {a1});
|
||||||
|
}), "call");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[](const entity& ent, const std::string& function,
|
||||||
|
const chaiscript::Boxed_Value& a1,
|
||||||
|
const chaiscript::Boxed_Value& a2)
|
||||||
|
{
|
||||||
|
return ent.call(function, {a1, a2});
|
||||||
|
}), "call");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[](const entity& ent, const std::string& function,
|
||||||
|
const chaiscript::Boxed_Value& a1,
|
||||||
|
const chaiscript::Boxed_Value& a2,
|
||||||
|
const chaiscript::Boxed_Value& a3)
|
||||||
|
{
|
||||||
|
return ent.call(function, {a1, a2, a3});
|
||||||
|
}), "call");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[](const entity& ent, const std::string& function,
|
||||||
|
const chaiscript::Boxed_Value& a1,
|
||||||
|
const chaiscript::Boxed_Value& a2,
|
||||||
|
const chaiscript::Boxed_Value& a3,
|
||||||
|
const chaiscript::Boxed_Value& a4)
|
||||||
|
{
|
||||||
|
return ent.call(function, {a1, a2, a3, a4});
|
||||||
|
}), "call");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[](const entity& ent, const std::string& function,
|
||||||
|
const chaiscript::Boxed_Value& a1,
|
||||||
|
const chaiscript::Boxed_Value& a2,
|
||||||
|
const chaiscript::Boxed_Value& a3,
|
||||||
|
const chaiscript::Boxed_Value& a4,
|
||||||
|
const chaiscript::Boxed_Value& a5)
|
||||||
|
{
|
||||||
|
return ent.call(function, {a1, a2, a3, a4, a5});
|
||||||
|
}), "call");
|
||||||
|
|
||||||
|
// Global call
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[context](const std::string& function,
|
||||||
|
const std::vector<chaiscript::Boxed_Value>& arguments)
|
||||||
|
{
|
||||||
|
return context->get_executer()->call(function, 0, arguments);
|
||||||
|
}), "vectorCall");
|
||||||
|
chai->add(chaiscript::fun([context](const std::string& function)
|
||||||
|
{
|
||||||
|
return context->get_executer()->call(function, 0, {});
|
||||||
|
}), "call");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[context](const std::string& function,
|
||||||
|
const chaiscript::Boxed_Value& a1)
|
||||||
|
{
|
||||||
|
return context->get_executer()->call(function, 0, {a1});
|
||||||
|
}), "call");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[context](const std::string& function,
|
||||||
|
const chaiscript::Boxed_Value& a1,
|
||||||
|
const chaiscript::Boxed_Value& a2)
|
||||||
|
{
|
||||||
|
return context->get_executer()->call(function, 0, {a1, a2});
|
||||||
|
}), "call");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[context](const std::string& function,
|
||||||
|
const chaiscript::Boxed_Value& a1,
|
||||||
|
const chaiscript::Boxed_Value& a2,
|
||||||
|
const chaiscript::Boxed_Value& a3)
|
||||||
|
{
|
||||||
|
return context->get_executer()->call(function, 0, {a1, a2, a3});
|
||||||
|
}), "call");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[context](const std::string& function,
|
||||||
|
const chaiscript::Boxed_Value& a1,
|
||||||
|
const chaiscript::Boxed_Value& a2,
|
||||||
|
const chaiscript::Boxed_Value& a3,
|
||||||
|
const chaiscript::Boxed_Value& a4)
|
||||||
|
{
|
||||||
|
return context->get_executer()->call(function, 0, {a1, a2, a3, a4});
|
||||||
|
}), "call");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun(
|
||||||
|
[context](const std::string& function,
|
||||||
|
const chaiscript::Boxed_Value& a1,
|
||||||
|
const chaiscript::Boxed_Value& a2,
|
||||||
|
const chaiscript::Boxed_Value& a3,
|
||||||
|
const chaiscript::Boxed_Value& a4,
|
||||||
|
const chaiscript::Boxed_Value& a5)
|
||||||
|
{
|
||||||
|
return context->get_executer()->call(function, 0, {a1, a2, a3, a4, a5});
|
||||||
|
}), "call");
|
||||||
|
}
|
||||||
|
|
||||||
|
void initialize(context* context)
|
||||||
|
{
|
||||||
|
initialize_entity(context);
|
||||||
|
|
||||||
|
const auto chai = context->get_chai();
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun([](const std::string& string)
|
||||||
|
{
|
||||||
|
printf("%s\n", string.data());
|
||||||
|
}), "print");
|
||||||
|
|
||||||
|
chai->add(chaiscript::fun([](const std::string& string)
|
||||||
|
{
|
||||||
|
MessageBoxA(nullptr, string.data(), nullptr, 0);
|
||||||
|
}), "alert");
|
||||||
|
|
||||||
|
const auto level_id = *game::native::levelEntityId;
|
||||||
|
chai->add_global(chaiscript::var(entity(context, level_id)), "level");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
src/game/scripting/context_initializer.hpp
Normal file
13
src/game/scripting/context_initializer.hpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "context.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
namespace context_initializer
|
||||||
|
{
|
||||||
|
void initialize(context* context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
82
src/game/scripting/entity.cpp
Normal file
82
src/game/scripting/entity.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#include "std_include.hpp"
|
||||||
|
#include "context.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
entity::entity() : entity(nullptr, 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
entity::entity(const entity& other) : entity(other.context_, other.entity_id_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
entity::entity(context* context, const unsigned int entity_id) : context_(context), entity_id_(entity_id)
|
||||||
|
{
|
||||||
|
if (this->entity_id_)
|
||||||
|
{
|
||||||
|
native::VariableValue value{};
|
||||||
|
value.type = native::SCRIPT_OBJECT;
|
||||||
|
value.u.entityId = this->entity_id_;
|
||||||
|
native::AddRefToValue(&value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entity::~entity()
|
||||||
|
{
|
||||||
|
if (this->entity_id_)
|
||||||
|
{
|
||||||
|
native::RemoveRefToValue(native::SCRIPT_OBJECT, {static_cast<int>(this->entity_id_)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void entity::on_notify(const std::string& event,
|
||||||
|
const std::function<void(const std::vector<chaiscript::Boxed_Value>&)>&
|
||||||
|
callback,
|
||||||
|
const bool is_volatile)
|
||||||
|
const
|
||||||
|
{
|
||||||
|
event_listener listener;
|
||||||
|
listener.event = event;
|
||||||
|
listener.callback = callback;
|
||||||
|
listener.entity_id = this->entity_id_;
|
||||||
|
listener.is_volatile = is_volatile;
|
||||||
|
|
||||||
|
this->context_->get_event_handler()->add_event_listener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int entity::get_entity_id() const
|
||||||
|
{
|
||||||
|
return this->entity_id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
native::scr_entref_t entity::get_entity_reference() const
|
||||||
|
{
|
||||||
|
return game::native::Scr_GetEntityIdRef(this->get_entity_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
chaiscript::Boxed_Value entity::call(const std::string& function,
|
||||||
|
const std::vector<chaiscript::Boxed_Value>& arguments) const
|
||||||
|
{
|
||||||
|
return this->context_->get_executer()->call(function, this->get_entity_id(), arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
void entity::notify(const std::string& event,
|
||||||
|
const std::vector<chaiscript::Boxed_Value>& arguments) const
|
||||||
|
{
|
||||||
|
this->context_->get_executer()->notify(event, this->get_entity_id(), arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
void entity::set(const std::string& field, const chaiscript::Boxed_Value& value) const
|
||||||
|
{
|
||||||
|
this->context_->get_executer()->set_entity_field(field, this->get_entity_id(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
chaiscript::Boxed_Value entity::get(const std::string& field) const
|
||||||
|
{
|
||||||
|
return this->context_->get_executer()->get_entity_field(field, this->get_entity_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
src/game/scripting/entity.hpp
Normal file
37
src/game/scripting/entity.hpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
class context;
|
||||||
|
|
||||||
|
class entity final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
entity();
|
||||||
|
entity(const entity& other);
|
||||||
|
entity(context* context, unsigned int entity_id);
|
||||||
|
~entity();
|
||||||
|
|
||||||
|
void on_notify(const std::string& event,
|
||||||
|
const std::function<void(const std::vector<chaiscript::Boxed_Value>&)>& callback,
|
||||||
|
bool is_volatile) const;
|
||||||
|
|
||||||
|
unsigned int get_entity_id() const;
|
||||||
|
game::native::scr_entref_t get_entity_reference() const;
|
||||||
|
|
||||||
|
chaiscript::Boxed_Value call(const std::string& function,
|
||||||
|
const std::vector<chaiscript::Boxed_Value>& arguments) const;
|
||||||
|
void notify(const std::string& event, const std::vector<chaiscript::Boxed_Value>& arguments) const;
|
||||||
|
|
||||||
|
void set(const std::string& field, const chaiscript::Boxed_Value& value) const;
|
||||||
|
chaiscript::Boxed_Value get(const std::string& field) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
context* context_;
|
||||||
|
unsigned int entity_id_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
16
src/game/scripting/event.hpp
Normal file
16
src/game/scripting/event.hpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
class event final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string name;
|
||||||
|
unsigned int entity_id;
|
||||||
|
std::vector<native::VariableValue> arguments;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
76
src/game/scripting/event_handler.cpp
Normal file
76
src/game/scripting/event_handler.cpp
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#include "std_include.hpp"
|
||||||
|
#include "context.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
event_handler::event_handler(context* context) : context_(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void event_handler::dispatch(event* event)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::vector<chaiscript::Boxed_Value> arguments;
|
||||||
|
|
||||||
|
for (const auto& argument : event->arguments)
|
||||||
|
{
|
||||||
|
arguments.push_back(this->context_->get_parameters()->load(argument));
|
||||||
|
}
|
||||||
|
|
||||||
|
this->dispatch_to_specific_listeners(event, arguments);
|
||||||
|
this->dispatch_to_generic_listeners(event, arguments);
|
||||||
|
}
|
||||||
|
catch (chaiscript::exception::eval_error& e)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(e.pretty_print());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void event_handler::dispatch_to_specific_listeners(event* event,
|
||||||
|
const std::vector<chaiscript::Boxed_Value>& arguments)
|
||||||
|
{
|
||||||
|
for (auto listener = this->event_listeners_.begin(); listener.is_valid(); ++listener)
|
||||||
|
{
|
||||||
|
if (listener->event == event->name && listener->entity_id == event->entity_id)
|
||||||
|
{
|
||||||
|
if (listener->is_volatile)
|
||||||
|
{
|
||||||
|
this->event_listeners_.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
listener->callback(arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void event_handler::dispatch_to_generic_listeners(event* event,
|
||||||
|
const std::vector<chaiscript::Boxed_Value>& arguments)
|
||||||
|
{
|
||||||
|
for (auto listener = this->generic_event_listeners_.begin(); listener.is_valid(); ++listener)
|
||||||
|
{
|
||||||
|
if (listener->event == event->name)
|
||||||
|
{
|
||||||
|
if (listener->is_volatile)
|
||||||
|
{
|
||||||
|
this->generic_event_listeners_.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
listener->callback(entity(this->context_, event->entity_id), arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void event_handler::add_event_listener(const event_listener& listener)
|
||||||
|
{
|
||||||
|
this->event_listeners_.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void event_handler::add_event_listener(const generic_event_listener& listener)
|
||||||
|
{
|
||||||
|
this->generic_event_listeners_.add(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
src/game/scripting/event_handler.hpp
Normal file
49
src/game/scripting/event_handler.hpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "utils/chain.hpp"
|
||||||
|
#include "entity.hpp"
|
||||||
|
#include "event.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
class context;
|
||||||
|
|
||||||
|
class event_listener final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string event = {};
|
||||||
|
unsigned int entity_id = 0;
|
||||||
|
std::function<void(const std::vector<chaiscript::Boxed_Value>&)> callback = {};
|
||||||
|
bool is_volatile = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class generic_event_listener final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string event = {};
|
||||||
|
std::function<void(const entity&, const std::vector<chaiscript::Boxed_Value>&)> callback = {};
|
||||||
|
bool is_volatile = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class event_handler final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit event_handler(context* context);
|
||||||
|
|
||||||
|
void dispatch(event* event);
|
||||||
|
|
||||||
|
void add_event_listener(const event_listener& listener);
|
||||||
|
void add_event_listener(const generic_event_listener& listener);
|
||||||
|
|
||||||
|
private:
|
||||||
|
context* context_;
|
||||||
|
|
||||||
|
void dispatch_to_specific_listeners(event* event, const std::vector<chaiscript::Boxed_Value>& arguments);
|
||||||
|
void dispatch_to_generic_listeners(event* event, const std::vector<chaiscript::Boxed_Value>& arguments);
|
||||||
|
|
||||||
|
utils::chain<event_listener> event_listeners_;
|
||||||
|
utils::chain<generic_event_listener> generic_event_listeners_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
172
src/game/scripting/executer.cpp
Normal file
172
src/game/scripting/executer.cpp
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
#include "std_include.hpp"
|
||||||
|
#include "game/game.hpp"
|
||||||
|
#include "utils/string.hpp"
|
||||||
|
#include "functions.hpp"
|
||||||
|
#include "stack_isolation.hpp"
|
||||||
|
#include "safe_executer.hpp"
|
||||||
|
#include "context.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
executer::executer(context* context) : context_(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int executer::get_field_id(const int classnum, const std::string& field) const
|
||||||
|
{
|
||||||
|
const auto field_name = utils::string::to_lower(field);
|
||||||
|
const auto class_id = native::g_classMap[classnum].id;
|
||||||
|
const auto field_str = native::SL_GetString(field_name.data(), 1);
|
||||||
|
const auto _ = gsl::finally([field_str]()
|
||||||
|
{
|
||||||
|
native::RemoveRefToValue(native::SCRIPT_STRING, {int(field_str)});
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto offset = native::FindVariable(class_id, field_str);
|
||||||
|
if (offset)
|
||||||
|
{
|
||||||
|
const auto index = 4 * (offset + 0xC800 * (class_id & 1));
|
||||||
|
return PINT(SELECT_VALUE(0x1A3BC80, 0x1EFE180, 0x1DC8800))[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void executer::set_entity_field(const std::string& field, const unsigned int entity_id,
|
||||||
|
const chaiscript::Boxed_Value& value)
|
||||||
|
{
|
||||||
|
const auto entref = native::Scr_GetEntityIdRef(entity_id);
|
||||||
|
const int id = get_field_id(entref.raw.classnum, field);
|
||||||
|
|
||||||
|
if (id != -1)
|
||||||
|
{
|
||||||
|
stack_isolation _;
|
||||||
|
this->context_->get_parameters()->push(value);
|
||||||
|
|
||||||
|
native::scr_VmPub->outparamcount = native::scr_VmPub->inparamcount;
|
||||||
|
native::scr_VmPub->inparamcount = 0;
|
||||||
|
|
||||||
|
if (!safe_executer::set_entity_field(entref, id))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to set value for field '" + field + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->entity_fields_[entity_id][field] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chaiscript::Boxed_Value executer::get_entity_field(const std::string& field, const unsigned int entity_id)
|
||||||
|
{
|
||||||
|
const auto entref = native::Scr_GetEntityIdRef(entity_id);
|
||||||
|
const auto id = this->get_field_id(entref.raw.classnum, field);
|
||||||
|
|
||||||
|
if (id != -1)
|
||||||
|
{
|
||||||
|
stack_isolation _;
|
||||||
|
|
||||||
|
native::VariableValue value{};
|
||||||
|
if (!safe_executer::get_entity_field(entref, id, &value))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to get value for field '" + field + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto $ = gsl::finally([value]()
|
||||||
|
{
|
||||||
|
native::RemoveRefToValue(value.type, value.u);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this->context_->get_parameters()->load(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto& map = this->entity_fields_[entity_id];
|
||||||
|
const auto value = map.find(field);
|
||||||
|
if (value != map.end())
|
||||||
|
{
|
||||||
|
return value->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void executer::notify(const std::string& event, const unsigned int entity_id,
|
||||||
|
std::vector<chaiscript::Boxed_Value> arguments) const
|
||||||
|
{
|
||||||
|
stack_isolation _;
|
||||||
|
|
||||||
|
std::reverse(arguments.begin(), arguments.end());
|
||||||
|
for (const auto& argument : arguments)
|
||||||
|
{
|
||||||
|
this->context_->get_parameters()->push(argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto event_id = native::SL_GetString(event.data(), 0);
|
||||||
|
native::Scr_NotifyId(entity_id, event_id, native::scr_VmPub->inparamcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
chaiscript::Boxed_Value executer::call(const std::string& function, const unsigned int entity_id,
|
||||||
|
std::vector<chaiscript::Boxed_Value> arguments) const
|
||||||
|
{
|
||||||
|
const auto function_index = find_function_index(function, entity_id == 0);
|
||||||
|
if (function_index < 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("No function found for name '" + function + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto entity = function_index > 0x1C7
|
||||||
|
? native::Scr_GetEntityIdRef(entity_id)
|
||||||
|
: native::scr_entref_t{~0u};
|
||||||
|
|
||||||
|
const auto function_ptr = native::Scr_GetFunc(function_index);
|
||||||
|
|
||||||
|
stack_isolation _;
|
||||||
|
|
||||||
|
std::reverse(arguments.begin(), arguments.end());
|
||||||
|
for (const auto& argument : arguments)
|
||||||
|
{
|
||||||
|
this->context_->get_parameters()->push(argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
native::scr_VmPub->outparamcount = native::scr_VmPub->inparamcount;
|
||||||
|
native::scr_VmPub->inparamcount = 0;
|
||||||
|
|
||||||
|
if (!safe_executer::call(function_ptr, entity))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Error executing function '" + function + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->context_->get_parameters()->get_return_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
int executer::find_function_index(const std::string& function, const bool prefer_global)
|
||||||
|
{
|
||||||
|
const auto target = utils::string::to_lower(function);
|
||||||
|
|
||||||
|
const auto primary_map = prefer_global
|
||||||
|
? &global_function_map
|
||||||
|
: &instance_function_map;
|
||||||
|
const auto secondary_map = !prefer_global
|
||||||
|
? &global_function_map
|
||||||
|
: &instance_function_map;
|
||||||
|
|
||||||
|
auto function_entry = primary_map->find(target);
|
||||||
|
if (function_entry != primary_map->end())
|
||||||
|
{
|
||||||
|
return function_entry->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
function_entry = secondary_map->find(target);
|
||||||
|
if (function_entry != secondary_map->end())
|
||||||
|
{
|
||||||
|
return function_entry->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
src/game/scripting/executer.hpp
Normal file
34
src/game/scripting/executer.hpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
class context;
|
||||||
|
|
||||||
|
class executer final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit executer(context* context);
|
||||||
|
|
||||||
|
void set_entity_field(const std::string& field, unsigned int entity_id,
|
||||||
|
const chaiscript::Boxed_Value& value);
|
||||||
|
chaiscript::Boxed_Value get_entity_field(const std::string& field, unsigned int entity_id);
|
||||||
|
|
||||||
|
void notify(const std::string& event, unsigned int entity_id,
|
||||||
|
std::vector<chaiscript::Boxed_Value> arguments) const;
|
||||||
|
|
||||||
|
chaiscript::Boxed_Value call(const std::string& function, unsigned int entity_id,
|
||||||
|
std::vector<chaiscript::Boxed_Value> arguments) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
context* context_;
|
||||||
|
|
||||||
|
std::unordered_map<unsigned int, std::unordered_map<std::string, chaiscript::Boxed_Value>> entity_fields_;
|
||||||
|
|
||||||
|
int get_field_id(int classnum, const std::string& field) const;
|
||||||
|
|
||||||
|
static int find_function_index(const std::string& function, bool prefer_global);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
152
src/game/scripting/parameters.cpp
Normal file
152
src/game/scripting/parameters.cpp
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
#include "std_include.hpp"
|
||||||
|
#include "context.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
parameters::parameters(context* context) : context_(context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
chaiscript::Boxed_Value parameters::load(const native::VariableValue value) const
|
||||||
|
{
|
||||||
|
if (value.type == native::SCRIPT_STRING)
|
||||||
|
{
|
||||||
|
const std::string string = native::SL_ConvertToString(value.u.stringValue);
|
||||||
|
return chaiscript::var(string);
|
||||||
|
}
|
||||||
|
else if (value.type == native::SCRIPT_FLOAT)
|
||||||
|
{
|
||||||
|
return chaiscript::var(value.u.floatValue);
|
||||||
|
}
|
||||||
|
else if (value.type == native::SCRIPT_INTEGER)
|
||||||
|
{
|
||||||
|
return chaiscript::var(value.u.intValue);
|
||||||
|
}
|
||||||
|
else if (value.type == native::SCRIPT_OBJECT)
|
||||||
|
{
|
||||||
|
return chaiscript::var(entity(this->context_, value.u.entityId));
|
||||||
|
}
|
||||||
|
else if (value.type == native::SCRIPT_VECTOR)
|
||||||
|
{
|
||||||
|
std::vector<chaiscript::Boxed_Value> values;
|
||||||
|
values.push_back(chaiscript::var(value.u.vectorValue[0]));
|
||||||
|
values.push_back(chaiscript::var(value.u.vectorValue[1]));
|
||||||
|
values.push_back(chaiscript::var(value.u.vectorValue[2]));
|
||||||
|
|
||||||
|
return chaiscript::var(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void parameters::push(const chaiscript::Boxed_Value& value) const
|
||||||
|
{
|
||||||
|
if (native::scr_VmPub->outparamcount)
|
||||||
|
{
|
||||||
|
native::Scr_ClearOutParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (native::scr_VmPub->top == native::scr_VmPub->maxstack)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Internal script stack overflow");
|
||||||
|
}
|
||||||
|
|
||||||
|
native::VariableValue* value_ptr = ++native::scr_VmPub->top;
|
||||||
|
++native::scr_VmPub->inparamcount;
|
||||||
|
|
||||||
|
value_ptr->type = native::SCRIPT_NONE;
|
||||||
|
value_ptr->u.intValue = 0;
|
||||||
|
|
||||||
|
if (value.get_type_info() == typeid(float))
|
||||||
|
{
|
||||||
|
const auto real_value = this->context_->get_chai()->boxed_cast<float>(value);
|
||||||
|
value_ptr->type = native::SCRIPT_FLOAT;
|
||||||
|
value_ptr->u.floatValue = real_value;
|
||||||
|
}
|
||||||
|
else if (value.get_type_info() == typeid(double))
|
||||||
|
{
|
||||||
|
const auto real_value = this->context_->get_chai()->boxed_cast<double>(value);
|
||||||
|
value_ptr->type = native::SCRIPT_FLOAT;
|
||||||
|
value_ptr->u.floatValue = static_cast<float>(real_value);
|
||||||
|
}
|
||||||
|
else if (value.get_type_info() == typeid(int))
|
||||||
|
{
|
||||||
|
const auto real_value = this->context_->get_chai()->boxed_cast<int>(value);
|
||||||
|
value_ptr->type = native::SCRIPT_INTEGER;
|
||||||
|
value_ptr->u.intValue = real_value;
|
||||||
|
}
|
||||||
|
else if (value.get_type_info() == typeid(bool))
|
||||||
|
{
|
||||||
|
const auto real_value = this->context_->get_chai()->boxed_cast<bool>(value);
|
||||||
|
value_ptr->type = native::SCRIPT_INTEGER;
|
||||||
|
value_ptr->u.intValue = real_value;
|
||||||
|
}
|
||||||
|
else if (value.get_type_info() == typeid(entity))
|
||||||
|
{
|
||||||
|
const auto real_value = this->context_->get_chai()->boxed_cast<entity>(value);
|
||||||
|
value_ptr->type = native::SCRIPT_OBJECT;
|
||||||
|
value_ptr->u.entityId = real_value.get_entity_id();
|
||||||
|
|
||||||
|
game::native::AddRefToValue(value_ptr);
|
||||||
|
}
|
||||||
|
else if (value.get_type_info() == typeid(std::string))
|
||||||
|
{
|
||||||
|
const auto real_value = this->context_->get_chai()->boxed_cast<std::string>(value);
|
||||||
|
value_ptr->type = native::SCRIPT_STRING;
|
||||||
|
value_ptr->u.stringValue = game::native::SL_GetString(real_value.data(), 0);
|
||||||
|
}
|
||||||
|
else if (value.get_type_info() == typeid(std::vector<chaiscript::Boxed_Value>))
|
||||||
|
{
|
||||||
|
float values[3];
|
||||||
|
const auto real_value = this->context_->get_chai()->boxed_cast<std::vector<chaiscript::Boxed_Value>>(value);
|
||||||
|
if (real_value.size() != 3)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Invalid vector length. Size must be exactly 3");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto unbox_float = [&real_value, this](const size_t index) -> float
|
||||||
|
{
|
||||||
|
const auto value = real_value[index];
|
||||||
|
if (value.get_type_info() == typeid(float))
|
||||||
|
{
|
||||||
|
return this->context_->get_chai()->boxed_cast<float>(value);
|
||||||
|
}
|
||||||
|
if (value.get_type_info() == typeid(double))
|
||||||
|
{
|
||||||
|
return float(this->context_->get_chai()->boxed_cast<double>(value));
|
||||||
|
}
|
||||||
|
if (value.get_type_info() == typeid(int))
|
||||||
|
{
|
||||||
|
return float(this->context_->get_chai()->boxed_cast<int>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Vector element at index " + std::to_string(index) + " is not a number");
|
||||||
|
};
|
||||||
|
|
||||||
|
values[0] = unbox_float(0);
|
||||||
|
values[1] = unbox_float(1);
|
||||||
|
values[2] = unbox_float(2);
|
||||||
|
|
||||||
|
value_ptr->type = native::SCRIPT_VECTOR;
|
||||||
|
value_ptr->u.vectorValue = native::Scr_AllocVector(values);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Unable to unbox value of type '" + value.get_type_info().bare_name() + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chaiscript::Boxed_Value parameters::get_return_value() const
|
||||||
|
{
|
||||||
|
if (native::scr_VmPub->inparamcount == 0) return {};
|
||||||
|
|
||||||
|
native::Scr_ClearOutParams();
|
||||||
|
native::scr_VmPub->outparamcount = native::scr_VmPub->inparamcount;
|
||||||
|
native::scr_VmPub->inparamcount = 0;
|
||||||
|
|
||||||
|
return this->load(native::scr_VmPub->top[1 - native::scr_VmPub->outparamcount]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
src/game/scripting/parameters.hpp
Normal file
23
src/game/scripting/parameters.hpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
class context;
|
||||||
|
|
||||||
|
class parameters final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit parameters(context* context);
|
||||||
|
|
||||||
|
void push(const chaiscript::Boxed_Value& value) const;
|
||||||
|
chaiscript::Boxed_Value load(native::VariableValue value) const;
|
||||||
|
|
||||||
|
chaiscript::Boxed_Value get_return_value() const;
|
||||||
|
private:
|
||||||
|
context* context_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
63
src/game/scripting/safe_executer.cpp
Normal file
63
src/game/scripting/safe_executer.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include "std_include.hpp"
|
||||||
|
#include "safe_executer.hpp"
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4611)
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
namespace safe_executer
|
||||||
|
{
|
||||||
|
static_assert(sizeof(jmp_buf) == 64);
|
||||||
|
|
||||||
|
bool call(const native::scr_call_t function, const native::scr_entref_t entref)
|
||||||
|
{
|
||||||
|
*native::g_script_error_level += 1;
|
||||||
|
if (setjmp(native::g_script_error[*native::g_script_error_level]))
|
||||||
|
{
|
||||||
|
*native::g_script_error_level -= 1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function(entref.val);
|
||||||
|
|
||||||
|
*native::g_script_error_level -= 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_entity_field(const native::scr_entref_t entref, const int offset)
|
||||||
|
{
|
||||||
|
*native::g_script_error_level += 1;
|
||||||
|
if (setjmp(native::g_script_error[*native::g_script_error_level]))
|
||||||
|
{
|
||||||
|
*native::g_script_error_level -= 1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
native::Scr_SetObjectField(entref.raw.classnum, entref.raw.entnum, offset);
|
||||||
|
|
||||||
|
*native::g_script_error_level -= 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get_entity_field(const native::scr_entref_t entref, const int offset, native::VariableValue* value)
|
||||||
|
{
|
||||||
|
*native::g_script_error_level += 1;
|
||||||
|
if (setjmp(native::g_script_error[*native::g_script_error_level]))
|
||||||
|
{
|
||||||
|
value->type = native::SCRIPT_NONE;
|
||||||
|
value->u.intValue = 0;
|
||||||
|
*native::g_script_error_level -= 1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = native::GetEntityFieldValue(entref.raw.classnum, entref.raw.entnum, offset);
|
||||||
|
|
||||||
|
*native::g_script_error_level -= 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#pragma warning(pop)
|
15
src/game/scripting/safe_executer.hpp
Normal file
15
src/game/scripting/safe_executer.hpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
namespace safe_executer
|
||||||
|
{
|
||||||
|
bool call(const native::scr_call_t function, const native::scr_entref_t entref);
|
||||||
|
bool set_entity_field(const native::scr_entref_t entref, const int offset);
|
||||||
|
bool get_entity_field(const native::scr_entref_t entref, const int offset, native::VariableValue* value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/game/scripting/stack_isolation.cpp
Normal file
30
src/game/scripting/stack_isolation.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include "std_include.hpp"
|
||||||
|
#include "stack_isolation.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
stack_isolation::stack_isolation()
|
||||||
|
{
|
||||||
|
this->in_param_count_ = native::scr_VmPub->inparamcount;
|
||||||
|
this->out_param_count_ = native::scr_VmPub->outparamcount;
|
||||||
|
this->top_ = native::scr_VmPub->top;
|
||||||
|
this->max_stack_ = native::scr_VmPub->maxstack;
|
||||||
|
|
||||||
|
native::scr_VmPub->top = this->stack_;
|
||||||
|
native::scr_VmPub->maxstack = &this->stack_[ARRAYSIZE(this->stack_) - 1];
|
||||||
|
native::scr_VmPub->inparamcount = 0;
|
||||||
|
native::scr_VmPub->outparamcount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_isolation::~stack_isolation()
|
||||||
|
{
|
||||||
|
native::Scr_ClearOutParams();
|
||||||
|
native::scr_VmPub->inparamcount = this->in_param_count_;
|
||||||
|
native::scr_VmPub->outparamcount = this->out_param_count_;
|
||||||
|
native::scr_VmPub->top = this->top_;
|
||||||
|
native::scr_VmPub->maxstack = this->max_stack_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
src/game/scripting/stack_isolation.hpp
Normal file
23
src/game/scripting/stack_isolation.hpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
class stack_isolation final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
stack_isolation();
|
||||||
|
~stack_isolation();
|
||||||
|
|
||||||
|
private:
|
||||||
|
native::VariableValue stack_[512]{};
|
||||||
|
|
||||||
|
native::VariableValue* max_stack_;
|
||||||
|
native::VariableValue* top_;
|
||||||
|
unsigned int in_param_count_;
|
||||||
|
unsigned int out_param_count_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
23
src/game/scripting/variable_value.cpp
Normal file
23
src/game/scripting/variable_value.cpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include "std_include.hpp"
|
||||||
|
#include "variable_value.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
variable_value::variable_value(native::VariableValue value) : value_(value)
|
||||||
|
{
|
||||||
|
native::AddRefToValue(&value);
|
||||||
|
}
|
||||||
|
|
||||||
|
variable_value::~variable_value()
|
||||||
|
{
|
||||||
|
native::RemoveRefToValue(this->value_.type, this->value_.u);
|
||||||
|
}
|
||||||
|
|
||||||
|
variable_value::operator native::VariableValue() const
|
||||||
|
{
|
||||||
|
return this->value_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
src/game/scripting/variable_value.hpp
Normal file
20
src/game/scripting/variable_value.hpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
namespace game
|
||||||
|
{
|
||||||
|
namespace scripting
|
||||||
|
{
|
||||||
|
class variable_value final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit variable_value(native::VariableValue value);
|
||||||
|
~variable_value();
|
||||||
|
|
||||||
|
explicit operator native::VariableValue() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
native::VariableValue value_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
#include "utils/hook.hpp"
|
#include "utils/hook.hpp"
|
||||||
|
|
||||||
std::mutex notification::mutex_;
|
std::mutex notification::mutex_;
|
||||||
std::vector<std::function<void(notification::event*)>> notification::callbacks_;
|
std::vector<std::function<void(game::scripting::event*)>> notification::callbacks_;
|
||||||
|
|
||||||
void notification::post_load()
|
void notification::post_load()
|
||||||
{
|
{
|
||||||
@ -25,7 +25,7 @@ void notification::pre_destroy()
|
|||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void notification::listen(const std::function<void(event*)>& callback)
|
void notification::listen(const std::function<void(game::scripting::event*)>& callback)
|
||||||
{
|
{
|
||||||
std::lock_guard _(mutex_);
|
std::lock_guard _(mutex_);
|
||||||
callbacks_.push_back(callback);
|
callbacks_.push_back(callback);
|
||||||
@ -37,7 +37,7 @@ void notification::cleanup()
|
|||||||
callbacks_.clear();
|
callbacks_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void notification::dispatch(event* event)
|
void notification::dispatch(game::scripting::event* event)
|
||||||
{
|
{
|
||||||
decltype(callbacks_) copy;
|
decltype(callbacks_) copy;
|
||||||
{
|
{
|
||||||
@ -56,7 +56,7 @@ void notification::vm_notify_stub(const unsigned int notify_id, const unsigned s
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
event e;
|
game::scripting::event e;
|
||||||
e.name = game::native::SL_ConvertToString(type);
|
e.name = game::native::SL_ConvertToString(type);
|
||||||
e.entity_id = notify_id;
|
e.entity_id = notify_id;
|
||||||
|
|
||||||
|
@ -1,30 +1,22 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "loader/module_loader.hpp"
|
#include "loader/module_loader.hpp"
|
||||||
|
#include "game/scripting/event.hpp"
|
||||||
#include "scripting.hpp"
|
#include "scripting.hpp"
|
||||||
|
|
||||||
class notification final : public module
|
class notification final : public module
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
class event final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::string name;
|
|
||||||
unsigned int entity_id;
|
|
||||||
//std::vector<scripting::variable> arguments;
|
|
||||||
std::vector<game::native::VariableValue> arguments;
|
|
||||||
};
|
|
||||||
|
|
||||||
void post_load() override;
|
void post_load() override;
|
||||||
void pre_destroy() override;
|
void pre_destroy() override;
|
||||||
|
|
||||||
static void listen(const std::function<void(event*)>& callback);
|
static void listen(const std::function<void(game::scripting::event*)>& callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::mutex mutex_;
|
static std::mutex mutex_;
|
||||||
static std::vector<std::function<void(event*)>> callbacks_;
|
static std::vector<std::function<void(game::scripting::event*)>> callbacks_;
|
||||||
|
|
||||||
static void cleanup();
|
static void cleanup();
|
||||||
static void dispatch(event* event);
|
static void dispatch(game::scripting::event* event);
|
||||||
|
|
||||||
static void vm_notify_stub(unsigned int notify_id, unsigned short type, game::native::VariableValue* stack);
|
static void vm_notify_stub(unsigned int notify_id, unsigned short type, game::native::VariableValue* stack);
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "notification.hpp"
|
#include "notification.hpp"
|
||||||
#include "utils/io.hpp"
|
#include "utils/io.hpp"
|
||||||
#include "utils/string.hpp"
|
|
||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
#include "game/scripting/functions.hpp"
|
|
||||||
|
|
||||||
utils::hook scripting::start_hook_;
|
utils::hook scripting::start_hook_;
|
||||||
utils::hook scripting::stop_hook_;
|
utils::hook scripting::stop_hook_;
|
||||||
@ -12,123 +10,20 @@ std::mutex scripting::mutex_;
|
|||||||
std::vector<std::function<void()>> scripting::start_callbacks_;
|
std::vector<std::function<void()>> scripting::start_callbacks_;
|
||||||
std::vector<std::function<void()>> scripting::stop_callbacks_;
|
std::vector<std::function<void()>> scripting::stop_callbacks_;
|
||||||
|
|
||||||
scripting::entity::entity() : entity(nullptr, 0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
scripting::entity::entity(const entity& other) : entity(other.environment_, other.entity_id_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
scripting::entity::entity(scripting* environment, const unsigned int entity_id) : environment_(environment),
|
|
||||||
entity_id_(entity_id)
|
|
||||||
{
|
|
||||||
if (this->entity_id_)
|
|
||||||
{
|
|
||||||
game::native::VariableValue value{};
|
|
||||||
value.type = game::native::SCRIPT_OBJECT;
|
|
||||||
value.u.entityId = this->entity_id_;
|
|
||||||
game::native::AddRefToValue(&value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scripting::entity::~entity()
|
|
||||||
{
|
|
||||||
if (this->entity_id_)
|
|
||||||
{
|
|
||||||
game::native::RemoveRefToValue(game::native::SCRIPT_OBJECT, {static_cast<int>(this->entity_id_)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::entity::on_notify(const std::string& event,
|
|
||||||
const std::function<void(const std::vector<chaiscript::Boxed_Value>&)>& callback,
|
|
||||||
const bool is_volatile)
|
|
||||||
const
|
|
||||||
{
|
|
||||||
event_listener listener;
|
|
||||||
listener.event = event;
|
|
||||||
listener.callback = callback;
|
|
||||||
listener.entity_id = this->entity_id_;
|
|
||||||
listener.is_volatile = is_volatile;
|
|
||||||
|
|
||||||
this->environment_->add_event_listener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int scripting::entity::get_entity_id() const
|
|
||||||
{
|
|
||||||
return this->entity_id_;
|
|
||||||
}
|
|
||||||
|
|
||||||
game::native::scr_entref_t scripting::entity::get_entity_reference() const
|
|
||||||
{
|
|
||||||
return game::native::Scr_GetEntityIdRef(this->get_entity_id());
|
|
||||||
}
|
|
||||||
|
|
||||||
chaiscript::Boxed_Value scripting::entity::call(const std::string& function,
|
|
||||||
const std::vector<chaiscript::Boxed_Value>& arguments) const
|
|
||||||
{
|
|
||||||
return this->environment_->call(function, this->get_entity_id(), arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::entity::notify(const std::string& event, const std::vector<chaiscript::Boxed_Value>& arguments) const
|
|
||||||
{
|
|
||||||
this->environment_->notify(event, this->get_entity_id(), arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::entity::set(const std::string& field, const chaiscript::Boxed_Value& value) const
|
|
||||||
{
|
|
||||||
this->environment_->set_entity_field(field, this->get_entity_id(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
chaiscript::Boxed_Value scripting::entity::get(const std::string& field) const
|
|
||||||
{
|
|
||||||
return this->environment_->get_entity_field(field, this->get_entity_id());
|
|
||||||
}
|
|
||||||
|
|
||||||
scripting::variable::variable(game::native::VariableValue value) : value_(value)
|
|
||||||
{
|
|
||||||
game::native::AddRefToValue(&value);
|
|
||||||
}
|
|
||||||
|
|
||||||
scripting::variable::~variable()
|
|
||||||
{
|
|
||||||
game::native::RemoveRefToValue(this->value_.type, this->value_.u);
|
|
||||||
}
|
|
||||||
|
|
||||||
scripting::variable::operator game::native::VariableValue() const
|
|
||||||
{
|
|
||||||
return this->value_;
|
|
||||||
}
|
|
||||||
|
|
||||||
scripting::stack_context::stack_context()
|
|
||||||
{
|
|
||||||
this->in_param_count_ = game::native::scr_VmPub->inparamcount;
|
|
||||||
this->out_param_count_ = game::native::scr_VmPub->outparamcount;
|
|
||||||
this->top_ = game::native::scr_VmPub->top;
|
|
||||||
this->max_stack_ = game::native::scr_VmPub->maxstack;
|
|
||||||
|
|
||||||
game::native::scr_VmPub->top = this->stack_;
|
|
||||||
game::native::scr_VmPub->maxstack = &this->stack_[ARRAYSIZE(this->stack_) - 1];
|
|
||||||
game::native::scr_VmPub->inparamcount = 0;
|
|
||||||
game::native::scr_VmPub->outparamcount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
scripting::stack_context::~stack_context()
|
|
||||||
{
|
|
||||||
game::native::Scr_ClearOutParams();
|
|
||||||
game::native::scr_VmPub->inparamcount = this->in_param_count_;
|
|
||||||
game::native::scr_VmPub->outparamcount = this->out_param_count_;
|
|
||||||
game::native::scr_VmPub->top = this->top_;
|
|
||||||
game::native::scr_VmPub->maxstack = this->max_stack_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::post_start()
|
void scripting::post_start()
|
||||||
{
|
{
|
||||||
on_start([this]()
|
on_start([this]()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this->initialize();
|
this->load_scripts();
|
||||||
|
notification::listen([this](game::scripting::event* event)
|
||||||
|
{
|
||||||
|
for(const auto& script : this->scripts_)
|
||||||
|
{
|
||||||
|
script->get_event_handler()->dispatch(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
catch (std::exception& e)
|
catch (std::exception& e)
|
||||||
{
|
{
|
||||||
@ -137,7 +32,7 @@ void scripting::post_start()
|
|||||||
});
|
});
|
||||||
on_stop([this]()
|
on_stop([this]()
|
||||||
{
|
{
|
||||||
this->chai_ = {};
|
this->scripts_.clear();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,290 +53,12 @@ void scripting::post_load()
|
|||||||
|
|
||||||
void scripting::pre_destroy()
|
void scripting::pre_destroy()
|
||||||
{
|
{
|
||||||
this->chai_ = {};
|
this->scripts_.clear();
|
||||||
start_callbacks_.clear();
|
start_callbacks_.clear();
|
||||||
stop_callbacks_.clear();
|
stop_callbacks_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void scripting::add_event_listener(const event_listener& listener)
|
void scripting::load_scripts()
|
||||||
{
|
|
||||||
this->event_listeners_.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::initialize()
|
|
||||||
{
|
|
||||||
this->chai_ = std::make_unique<chaiscript::ChaiScript>();
|
|
||||||
this->chai_->add(chaiscript::fun([](const std::string& string)
|
|
||||||
{
|
|
||||||
printf("%s\n", string.data());
|
|
||||||
}), "print");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun([](const std::string& string)
|
|
||||||
{
|
|
||||||
MessageBoxA(nullptr, string.data(), nullptr, 0);
|
|
||||||
}), "alert");
|
|
||||||
|
|
||||||
this->initialize_entity();
|
|
||||||
|
|
||||||
const auto level_id = *game::native::levelEntityId;
|
|
||||||
this->chai_->add_global(chaiscript::var(entity(this, level_id)), "level");
|
|
||||||
|
|
||||||
this->load_scripts();
|
|
||||||
|
|
||||||
notification::listen([this](notification::event* event)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::vector<chaiscript::Boxed_Value> arguments;
|
|
||||||
|
|
||||||
for (const auto& argument : event->arguments)
|
|
||||||
{
|
|
||||||
arguments.push_back(this->make_boxed(argument));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto listener = this->event_listeners_.begin(); listener.is_valid(); ++listener)
|
|
||||||
{
|
|
||||||
if (listener->event == event->name && listener->entity_id == event->entity_id)
|
|
||||||
{
|
|
||||||
if (listener->is_volatile)
|
|
||||||
{
|
|
||||||
this->event_listeners_.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
listener->callback(arguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto listener = this->generic_event_listeners_.begin(); listener.is_valid(); ++listener)
|
|
||||||
{
|
|
||||||
if (listener->event == event->name)
|
|
||||||
{
|
|
||||||
if (listener->is_volatile)
|
|
||||||
{
|
|
||||||
this->generic_event_listeners_.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
listener->callback(entity(this, event->entity_id), arguments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (chaiscript::exception::eval_error& e)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(e.pretty_print());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::initialize_entity()
|
|
||||||
{
|
|
||||||
this->chai_->add(chaiscript::user_type<entity>(), "entity");
|
|
||||||
this->chai_->add(chaiscript::constructor<entity()>(), "entity");
|
|
||||||
this->chai_->add(chaiscript::constructor<entity(const entity&)>(), "entity");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(&entity::get), "get");
|
|
||||||
this->chai_->add(chaiscript::fun(&entity::set), "set");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(&entity::on_notify), "onNotify");
|
|
||||||
this->chai_->add(chaiscript::fun([](const entity& ent, const std::string& event,
|
|
||||||
const std::function<void(const std::vector<chaiscript::Boxed_Value>&)>&
|
|
||||||
callback)
|
|
||||||
{
|
|
||||||
return ent.on_notify(event, callback, false);
|
|
||||||
}), "onNotify");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun([](entity& lhs, const entity& rhs) -> entity&
|
|
||||||
{
|
|
||||||
return lhs = rhs;
|
|
||||||
}), "=");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun([this](const std::string& event,
|
|
||||||
const std::function<void(const entity&,
|
|
||||||
const std::vector<chaiscript::Boxed_Value>&)>&
|
|
||||||
callback)
|
|
||||||
{
|
|
||||||
generic_event_listener listener;
|
|
||||||
listener.event = event;
|
|
||||||
listener.is_volatile = false;
|
|
||||||
listener.callback = callback;
|
|
||||||
|
|
||||||
this->generic_event_listeners_.add(listener);
|
|
||||||
}), "onNotify");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun([this](const std::string& event,
|
|
||||||
const std::function<void(const entity&,
|
|
||||||
const std::vector<chaiscript::Boxed_Value>&)>&
|
|
||||||
callback, const bool is_volatile)
|
|
||||||
{
|
|
||||||
generic_event_listener listener;
|
|
||||||
listener.event = event;
|
|
||||||
listener.is_volatile = is_volatile;
|
|
||||||
listener.callback = callback;
|
|
||||||
|
|
||||||
this->generic_event_listeners_.add(listener);
|
|
||||||
}), "onNotify");
|
|
||||||
|
|
||||||
// Notification
|
|
||||||
this->chai_->add(chaiscript::fun(&entity::notify), "vectorNotify");
|
|
||||||
this->chai_->add(chaiscript::fun([](const entity& ent, const std::string& event)
|
|
||||||
{
|
|
||||||
return ent.notify(event, {});
|
|
||||||
}), "notify");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[](const entity& ent, const std::string& event,
|
|
||||||
const chaiscript::Boxed_Value& a1)
|
|
||||||
{
|
|
||||||
return ent.notify(event, {a1});
|
|
||||||
}), "notify");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[](const entity& ent, const std::string& event,
|
|
||||||
const chaiscript::Boxed_Value& a1,
|
|
||||||
const chaiscript::Boxed_Value& a2)
|
|
||||||
{
|
|
||||||
return ent.notify(event, {a1, a2});
|
|
||||||
}), "notify");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[](const entity& ent, const std::string& event,
|
|
||||||
const chaiscript::Boxed_Value& a1,
|
|
||||||
const chaiscript::Boxed_Value& a2,
|
|
||||||
const chaiscript::Boxed_Value& a3)
|
|
||||||
{
|
|
||||||
return ent.notify(event, {a1, a2, a3});
|
|
||||||
}), "notify");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[](const entity& ent, const std::string& event,
|
|
||||||
const chaiscript::Boxed_Value& a1,
|
|
||||||
const chaiscript::Boxed_Value& a2,
|
|
||||||
const chaiscript::Boxed_Value& a3,
|
|
||||||
const chaiscript::Boxed_Value& a4)
|
|
||||||
{
|
|
||||||
return ent.notify(event, {a1, a2, a3, a4});
|
|
||||||
}), "notify");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[](const entity& ent, const std::string& event,
|
|
||||||
const chaiscript::Boxed_Value& a1,
|
|
||||||
const chaiscript::Boxed_Value& a2,
|
|
||||||
const chaiscript::Boxed_Value& a3,
|
|
||||||
const chaiscript::Boxed_Value& a4,
|
|
||||||
const chaiscript::Boxed_Value& a5)
|
|
||||||
{
|
|
||||||
return ent.notify(event, {a1, a2, a3, a4, a5});
|
|
||||||
}), "notify");
|
|
||||||
|
|
||||||
// Instance call
|
|
||||||
this->chai_->add(chaiscript::fun(&entity::call), "vectorCall");
|
|
||||||
this->chai_->add(chaiscript::fun([](const entity& ent, const std::string& function)
|
|
||||||
{
|
|
||||||
return ent.call(function, {});
|
|
||||||
}), "call");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[](const entity& ent, const std::string& function,
|
|
||||||
const chaiscript::Boxed_Value& a1)
|
|
||||||
{
|
|
||||||
return ent.call(function, {a1});
|
|
||||||
}), "call");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[](const entity& ent, const std::string& function,
|
|
||||||
const chaiscript::Boxed_Value& a1,
|
|
||||||
const chaiscript::Boxed_Value& a2)
|
|
||||||
{
|
|
||||||
return ent.call(function, {a1, a2});
|
|
||||||
}), "call");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[](const entity& ent, const std::string& function,
|
|
||||||
const chaiscript::Boxed_Value& a1,
|
|
||||||
const chaiscript::Boxed_Value& a2,
|
|
||||||
const chaiscript::Boxed_Value& a3)
|
|
||||||
{
|
|
||||||
return ent.call(function, {a1, a2, a3});
|
|
||||||
}), "call");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[](const entity& ent, const std::string& function,
|
|
||||||
const chaiscript::Boxed_Value& a1,
|
|
||||||
const chaiscript::Boxed_Value& a2,
|
|
||||||
const chaiscript::Boxed_Value& a3,
|
|
||||||
const chaiscript::Boxed_Value& a4)
|
|
||||||
{
|
|
||||||
return ent.call(function, {a1, a2, a3, a4});
|
|
||||||
}), "call");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[](const entity& ent, const std::string& function,
|
|
||||||
const chaiscript::Boxed_Value& a1,
|
|
||||||
const chaiscript::Boxed_Value& a2,
|
|
||||||
const chaiscript::Boxed_Value& a3,
|
|
||||||
const chaiscript::Boxed_Value& a4,
|
|
||||||
const chaiscript::Boxed_Value& a5)
|
|
||||||
{
|
|
||||||
return ent.call(function, {a1, a2, a3, a4, a5});
|
|
||||||
}), "call");
|
|
||||||
|
|
||||||
// Global call
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[this](const std::string& function, const std::vector<chaiscript::Boxed_Value>& arguments)
|
|
||||||
{
|
|
||||||
return this->call(function, 0, arguments);
|
|
||||||
}), "vectorCall");
|
|
||||||
this->chai_->add(chaiscript::fun([this](const std::string& function)
|
|
||||||
{
|
|
||||||
return this->call(function, 0, {});
|
|
||||||
}), "call");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[this](const std::string& function,
|
|
||||||
const chaiscript::Boxed_Value& a1)
|
|
||||||
{
|
|
||||||
return this->call(function, 0, {a1});
|
|
||||||
}), "call");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[this](const std::string& function,
|
|
||||||
const chaiscript::Boxed_Value& a1,
|
|
||||||
const chaiscript::Boxed_Value& a2)
|
|
||||||
{
|
|
||||||
return this->call(function, 0, {a1, a2});
|
|
||||||
}), "call");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[this](const std::string& function,
|
|
||||||
const chaiscript::Boxed_Value& a1,
|
|
||||||
const chaiscript::Boxed_Value& a2,
|
|
||||||
const chaiscript::Boxed_Value& a3)
|
|
||||||
{
|
|
||||||
return this->call(function, 0, {a1, a2, a3});
|
|
||||||
}), "call");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[this](const std::string& function,
|
|
||||||
const chaiscript::Boxed_Value& a1,
|
|
||||||
const chaiscript::Boxed_Value& a2,
|
|
||||||
const chaiscript::Boxed_Value& a3,
|
|
||||||
const chaiscript::Boxed_Value& a4)
|
|
||||||
{
|
|
||||||
return this->call(function, 0, {a1, a2, a3, a4});
|
|
||||||
}), "call");
|
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
|
||||||
[this](const std::string& function,
|
|
||||||
const chaiscript::Boxed_Value& a1,
|
|
||||||
const chaiscript::Boxed_Value& a2,
|
|
||||||
const chaiscript::Boxed_Value& a3,
|
|
||||||
const chaiscript::Boxed_Value& a4,
|
|
||||||
const chaiscript::Boxed_Value& a5)
|
|
||||||
{
|
|
||||||
return this->call(function, 0, {a1, a2, a3, a4, a5});
|
|
||||||
}), "call");
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::load_scripts() const
|
|
||||||
{
|
{
|
||||||
const auto scripts = utils::io::list_files("open-iw5/scripts/");
|
const auto scripts = utils::io::list_files("open-iw5/scripts/");
|
||||||
|
|
||||||
@ -451,7 +68,9 @@ void scripting::load_scripts() const
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this->chai_->eval_file(script);
|
auto context = std::make_unique<game::scripting::context>();
|
||||||
|
context->get_chai()->eval_file(script);
|
||||||
|
this->scripts_.push_back(std::move(context));
|
||||||
}
|
}
|
||||||
catch (chaiscript::exception::eval_error& e)
|
catch (chaiscript::exception::eval_error& e)
|
||||||
{
|
{
|
||||||
@ -461,38 +80,6 @@ void scripting::load_scripts() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chaiscript::Boxed_Value scripting::make_boxed(const game::native::VariableValue value)
|
|
||||||
{
|
|
||||||
if (value.type == game::native::SCRIPT_STRING)
|
|
||||||
{
|
|
||||||
const std::string string = game::native::SL_ConvertToString(value.u.stringValue);
|
|
||||||
return chaiscript::var(string);
|
|
||||||
}
|
|
||||||
else if (value.type == game::native::SCRIPT_FLOAT)
|
|
||||||
{
|
|
||||||
return chaiscript::var(value.u.floatValue);
|
|
||||||
}
|
|
||||||
else if (value.type == game::native::SCRIPT_INTEGER)
|
|
||||||
{
|
|
||||||
return chaiscript::var(value.u.intValue);
|
|
||||||
}
|
|
||||||
else if (value.type == game::native::SCRIPT_OBJECT)
|
|
||||||
{
|
|
||||||
return chaiscript::var(entity(this, value.u.entityId));
|
|
||||||
}
|
|
||||||
else if (value.type == game::native::SCRIPT_VECTOR)
|
|
||||||
{
|
|
||||||
std::vector<chaiscript::Boxed_Value> values;
|
|
||||||
values.push_back(chaiscript::var(value.u.vectorValue[0]));
|
|
||||||
values.push_back(chaiscript::var(value.u.vectorValue[1]));
|
|
||||||
values.push_back(chaiscript::var(value.u.vectorValue[2]));
|
|
||||||
|
|
||||||
return chaiscript::var(values);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::on_start(const std::function<void()>& callback)
|
void scripting::on_start(const std::function<void()>& callback)
|
||||||
{
|
{
|
||||||
std::lock_guard _(mutex_);
|
std::lock_guard _(mutex_);
|
||||||
@ -543,320 +130,4 @@ void scripting::stop_execution()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int scripting::get_field_id(const int classnum, const std::string& field) const
|
|
||||||
{
|
|
||||||
const auto field_name = utils::string::to_lower(field);
|
|
||||||
const auto class_id = game::native::g_classMap[classnum].id;
|
|
||||||
const auto field_str = game::native::SL_GetString(field_name.data(), 1);
|
|
||||||
const auto _ = gsl::finally([field_str]()
|
|
||||||
{
|
|
||||||
game::native::RemoveRefToValue(game::native::SCRIPT_STRING, {int(field_str)});
|
|
||||||
});
|
|
||||||
|
|
||||||
const auto offset = game::native::FindVariable(class_id, field_str);
|
|
||||||
if (offset)
|
|
||||||
{
|
|
||||||
const auto index = 4 * (offset + 0xC800 * (class_id & 1));
|
|
||||||
return PINT(SELECT_VALUE(0x1A3BC80, 0x1EFE180, 0x1DC8800))[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::set_entity_field(const std::string& field, const unsigned int entity_id,
|
|
||||||
const chaiscript::Boxed_Value& value)
|
|
||||||
{
|
|
||||||
const auto entref = game::native::Scr_GetEntityIdRef(entity_id);
|
|
||||||
const int id = get_field_id(entref.raw.classnum, field);
|
|
||||||
|
|
||||||
if (id != -1)
|
|
||||||
{
|
|
||||||
stack_context _;
|
|
||||||
this->push_param(value);
|
|
||||||
|
|
||||||
game::native::scr_VmPub->outparamcount = game::native::scr_VmPub->inparamcount;
|
|
||||||
game::native::scr_VmPub->inparamcount = 0;
|
|
||||||
|
|
||||||
if (!set_entity_field_safe(entref, id))
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Failed to set value for field '" + field + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this->entity_fields_[entity_id][field] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chaiscript::Boxed_Value scripting::get_entity_field(const std::string& field, const unsigned int entity_id)
|
|
||||||
{
|
|
||||||
const auto entref = game::native::Scr_GetEntityIdRef(entity_id);
|
|
||||||
const auto id = this->get_field_id(entref.raw.classnum, field);
|
|
||||||
|
|
||||||
if (id != -1)
|
|
||||||
{
|
|
||||||
stack_context _;
|
|
||||||
|
|
||||||
game::native::VariableValue value{};
|
|
||||||
if (!get_entity_field_safe(entref, id, &value))
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Failed to get value for field '" + field + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto $ = gsl::finally([value]()
|
|
||||||
{
|
|
||||||
game::native::RemoveRefToValue(value.type, value.u);
|
|
||||||
});
|
|
||||||
|
|
||||||
return this->make_boxed(value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto& map = this->entity_fields_[entity_id];
|
|
||||||
const auto value = map.find(field);
|
|
||||||
if (value != map.end())
|
|
||||||
{
|
|
||||||
return value->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::notify(const std::string& event, const unsigned int entity_id,
|
|
||||||
std::vector<chaiscript::Boxed_Value> arguments) const
|
|
||||||
{
|
|
||||||
stack_context _;
|
|
||||||
|
|
||||||
std::reverse(arguments.begin(), arguments.end());
|
|
||||||
for (const auto& argument : arguments)
|
|
||||||
{
|
|
||||||
this->push_param(argument);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto event_id = game::native::SL_GetString(event.data(), 0);
|
|
||||||
game::native::Scr_NotifyId(entity_id, event_id, game::native::scr_VmPub->inparamcount);
|
|
||||||
}
|
|
||||||
|
|
||||||
chaiscript::Boxed_Value scripting::call(const std::string& function, const unsigned int entity_id,
|
|
||||||
std::vector<chaiscript::Boxed_Value> arguments)
|
|
||||||
{
|
|
||||||
const auto function_index = find_function_index(function, entity_id == 0);
|
|
||||||
if (function_index < 0)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("No function found for name '" + function + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto entity = function_index > 0x1C7
|
|
||||||
? game::native::Scr_GetEntityIdRef(entity_id)
|
|
||||||
: game::native::scr_entref_t{~0u};
|
|
||||||
|
|
||||||
const auto function_ptr = game::native::Scr_GetFunc(function_index);
|
|
||||||
|
|
||||||
stack_context _;
|
|
||||||
|
|
||||||
std::reverse(arguments.begin(), arguments.end());
|
|
||||||
for (const auto& argument : arguments)
|
|
||||||
{
|
|
||||||
this->push_param(argument);
|
|
||||||
}
|
|
||||||
|
|
||||||
game::native::scr_VmPub->outparamcount = game::native::scr_VmPub->inparamcount;
|
|
||||||
game::native::scr_VmPub->inparamcount = 0;
|
|
||||||
|
|
||||||
if (!call_safe(function_ptr, entity))
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Error executing function '" + function + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this->get_return_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable: 4611)
|
|
||||||
bool scripting::call_safe(const game::native::scr_call_t function, const game::native::scr_entref_t entref)
|
|
||||||
{
|
|
||||||
static_assert(sizeof(jmp_buf) == 64);
|
|
||||||
|
|
||||||
*game::native::g_script_error_level += 1;
|
|
||||||
if (setjmp(game::native::g_script_error[*game::native::g_script_error_level]))
|
|
||||||
{
|
|
||||||
*game::native::g_script_error_level -= 1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function(entref.val);
|
|
||||||
|
|
||||||
*game::native::g_script_error_level -= 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool scripting::set_entity_field_safe(game::native::scr_entref_t entref, int offset)
|
|
||||||
{
|
|
||||||
*game::native::g_script_error_level += 1;
|
|
||||||
if (setjmp(game::native::g_script_error[*game::native::g_script_error_level]))
|
|
||||||
{
|
|
||||||
*game::native::g_script_error_level -= 1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
game::native::Scr_SetObjectField(entref.raw.classnum, entref.raw.entnum, offset);
|
|
||||||
|
|
||||||
*game::native::g_script_error_level -= 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool scripting::get_entity_field_safe(game::native::scr_entref_t entref, int offset, game::native::VariableValue* value)
|
|
||||||
{
|
|
||||||
*game::native::g_script_error_level += 1;
|
|
||||||
if (setjmp(game::native::g_script_error[*game::native::g_script_error_level]))
|
|
||||||
{
|
|
||||||
value->type = game::native::SCRIPT_NONE;
|
|
||||||
value->u.intValue = 0;
|
|
||||||
*game::native::g_script_error_level -= 1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*value = game::native::GetEntityFieldValue(entref.raw.classnum, entref.raw.entnum, offset);
|
|
||||||
|
|
||||||
*game::native::g_script_error_level -= 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#pragma warning(pop)
|
|
||||||
|
|
||||||
int scripting::find_function_index(const std::string& function, const bool prefer_global)
|
|
||||||
{
|
|
||||||
const auto target = utils::string::to_lower(function);
|
|
||||||
|
|
||||||
const auto primary_map = prefer_global
|
|
||||||
? &game::scripting::global_function_map
|
|
||||||
: &game::scripting::instance_function_map;
|
|
||||||
const auto secondary_map = !prefer_global
|
|
||||||
? &game::scripting::global_function_map
|
|
||||||
: &game::scripting::instance_function_map;
|
|
||||||
|
|
||||||
auto function_entry = primary_map->find(target);
|
|
||||||
if (function_entry != primary_map->end())
|
|
||||||
{
|
|
||||||
return function_entry->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
function_entry = secondary_map->find(target);
|
|
||||||
if (function_entry != secondary_map->end())
|
|
||||||
{
|
|
||||||
return function_entry->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void scripting::push_param(const chaiscript::Boxed_Value& value) const
|
|
||||||
{
|
|
||||||
if (game::native::scr_VmPub->outparamcount)
|
|
||||||
{
|
|
||||||
game::native::Scr_ClearOutParams();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game::native::scr_VmPub->top == game::native::scr_VmPub->maxstack)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Internal script stack overflow");
|
|
||||||
}
|
|
||||||
|
|
||||||
game::native::VariableValue* value_ptr = ++game::native::scr_VmPub->top;
|
|
||||||
++game::native::scr_VmPub->inparamcount;
|
|
||||||
|
|
||||||
value_ptr->type = game::native::SCRIPT_NONE;
|
|
||||||
value_ptr->u.intValue = 0;
|
|
||||||
|
|
||||||
if (value.get_type_info() == typeid(float))
|
|
||||||
{
|
|
||||||
const auto real_value = this->chai_->boxed_cast<float>(value);
|
|
||||||
value_ptr->type = game::native::SCRIPT_FLOAT;
|
|
||||||
value_ptr->u.floatValue = real_value;
|
|
||||||
}
|
|
||||||
else if (value.get_type_info() == typeid(double))
|
|
||||||
{
|
|
||||||
const auto real_value = this->chai_->boxed_cast<double>(value);
|
|
||||||
value_ptr->type = game::native::SCRIPT_FLOAT;
|
|
||||||
value_ptr->u.floatValue = static_cast<float>(real_value);
|
|
||||||
}
|
|
||||||
else if (value.get_type_info() == typeid(int))
|
|
||||||
{
|
|
||||||
const auto real_value = this->chai_->boxed_cast<int>(value);
|
|
||||||
value_ptr->type = game::native::SCRIPT_INTEGER;
|
|
||||||
value_ptr->u.intValue = real_value;
|
|
||||||
}
|
|
||||||
else if (value.get_type_info() == typeid(bool))
|
|
||||||
{
|
|
||||||
const auto real_value = this->chai_->boxed_cast<bool>(value);
|
|
||||||
value_ptr->type = game::native::SCRIPT_INTEGER;
|
|
||||||
value_ptr->u.intValue = real_value;
|
|
||||||
}
|
|
||||||
else if (value.get_type_info() == typeid(entity))
|
|
||||||
{
|
|
||||||
const auto real_value = this->chai_->boxed_cast<entity>(value);
|
|
||||||
value_ptr->type = game::native::SCRIPT_OBJECT;
|
|
||||||
value_ptr->u.entityId = real_value.get_entity_id();
|
|
||||||
|
|
||||||
game::native::AddRefToValue(value_ptr);
|
|
||||||
}
|
|
||||||
else if (value.get_type_info() == typeid(std::string))
|
|
||||||
{
|
|
||||||
const auto real_value = this->chai_->boxed_cast<std::string>(value);
|
|
||||||
value_ptr->type = game::native::SCRIPT_STRING;
|
|
||||||
value_ptr->u.stringValue = game::native::SL_GetString(real_value.data(), 0);
|
|
||||||
}
|
|
||||||
else if (value.get_type_info() == typeid(std::vector<chaiscript::Boxed_Value>))
|
|
||||||
{
|
|
||||||
float values[3];
|
|
||||||
const auto real_value = this->chai_->boxed_cast<std::vector<chaiscript::Boxed_Value>>(value);
|
|
||||||
if (real_value.size() != 3)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Invalid vector length. Size must be exactly 3");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto unbox_float = [&real_value, this](const size_t index) -> float
|
|
||||||
{
|
|
||||||
const auto value = real_value[index];
|
|
||||||
if (value.get_type_info() == typeid(float))
|
|
||||||
{
|
|
||||||
return this->chai_->boxed_cast<float>(value);
|
|
||||||
}
|
|
||||||
else if (value.get_type_info() == typeid(double))
|
|
||||||
{
|
|
||||||
return float(this->chai_->boxed_cast<double>(value));
|
|
||||||
}
|
|
||||||
else if (value.get_type_info() == typeid(int))
|
|
||||||
{
|
|
||||||
return float(this->chai_->boxed_cast<int>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("Vector element at index " + std::to_string(index) + " is not a number");
|
|
||||||
};
|
|
||||||
|
|
||||||
values[0] = unbox_float(0);
|
|
||||||
values[1] = unbox_float(1);
|
|
||||||
values[2] = unbox_float(2);
|
|
||||||
|
|
||||||
value_ptr->type = game::native::SCRIPT_VECTOR;
|
|
||||||
value_ptr->u.vectorValue = game::native::Scr_AllocVector(values);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Unable to unbox value of type '" + value.get_type_info().bare_name() + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chaiscript::Boxed_Value scripting::get_return_value()
|
|
||||||
{
|
|
||||||
if (game::native::scr_VmPub->inparamcount == 0) return {};
|
|
||||||
|
|
||||||
game::native::Scr_ClearOutParams();
|
|
||||||
game::native::scr_VmPub->outparamcount = game::native::scr_VmPub->inparamcount;
|
|
||||||
game::native::scr_VmPub->inparamcount = 0;
|
|
||||||
|
|
||||||
return this->make_boxed(game::native::scr_VmPub->top[1 - game::native::scr_VmPub->outparamcount]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
REGISTER_MODULE(scripting)
|
REGISTER_MODULE(scripting)
|
||||||
|
@ -1,67 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "loader/module_loader.hpp"
|
#include "loader/module_loader.hpp"
|
||||||
#include "game/game.hpp"
|
|
||||||
#include "utils/hook.hpp"
|
#include "utils/hook.hpp"
|
||||||
#include "utils/chain.hpp"
|
#include "game/scripting/context.hpp"
|
||||||
|
|
||||||
class scripting final : public module
|
class scripting final : public module
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
class entity final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
entity();
|
|
||||||
entity(const entity& other);
|
|
||||||
entity(scripting* environment, unsigned int entity_id);
|
|
||||||
~entity();
|
|
||||||
|
|
||||||
void on_notify(const std::string& event,
|
|
||||||
const std::function<void(const std::vector<chaiscript::Boxed_Value>&)>& callback,
|
|
||||||
bool is_volatile) const;
|
|
||||||
|
|
||||||
unsigned int get_entity_id() const;
|
|
||||||
game::native::scr_entref_t get_entity_reference() const;
|
|
||||||
|
|
||||||
chaiscript::Boxed_Value call(const std::string& function, const std::vector<chaiscript::Boxed_Value>& arguments) const;
|
|
||||||
void notify(const std::string& event, const std::vector<chaiscript::Boxed_Value>& arguments) const;
|
|
||||||
|
|
||||||
void set(const std::string& field, const chaiscript::Boxed_Value& value) const;
|
|
||||||
chaiscript::Boxed_Value get(const std::string& field) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
scripting* environment_;
|
|
||||||
unsigned int entity_id_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class variable final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
variable(game::native::VariableValue value);
|
|
||||||
~variable();
|
|
||||||
|
|
||||||
operator game::native::VariableValue() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
game::native::VariableValue value_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class event_listener final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::string event = {};
|
|
||||||
unsigned int entity_id = 0;
|
|
||||||
std::function<void(const std::vector<chaiscript::Boxed_Value>&)> callback = {};
|
|
||||||
bool is_volatile = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
class generic_event_listener final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::string event = {};
|
|
||||||
std::function<void(const entity&, const std::vector<chaiscript::Boxed_Value>&)> callback = {};
|
|
||||||
bool is_volatile = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
void post_start() override;
|
void post_start() override;
|
||||||
void post_load() override;
|
void post_load() override;
|
||||||
void pre_destroy() override;
|
void pre_destroy() override;
|
||||||
@ -72,34 +16,9 @@ public:
|
|||||||
static void propagate_error(const std::exception& e);
|
static void propagate_error(const std::exception& e);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class stack_context final
|
std::vector<std::unique_ptr<game::scripting::context>> scripts_;
|
||||||
{
|
|
||||||
public:
|
|
||||||
stack_context();
|
|
||||||
~stack_context();
|
|
||||||
|
|
||||||
private:
|
void load_scripts();
|
||||||
game::native::VariableValue stack_[512]{};
|
|
||||||
|
|
||||||
game::native::VariableValue *max_stack_;
|
|
||||||
game::native::VariableValue *top_;
|
|
||||||
unsigned int in_param_count_;
|
|
||||||
unsigned int out_param_count_;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<chaiscript::ChaiScript> chai_;
|
|
||||||
utils::chain<event_listener> event_listeners_;
|
|
||||||
utils::chain<generic_event_listener> generic_event_listeners_;
|
|
||||||
|
|
||||||
std::unordered_map<unsigned int, std::unordered_map<std::string, chaiscript::Boxed_Value>> entity_fields_;
|
|
||||||
|
|
||||||
void add_event_listener(const event_listener& listener);
|
|
||||||
|
|
||||||
void initialize();
|
|
||||||
void initialize_entity();
|
|
||||||
void load_scripts() const;
|
|
||||||
|
|
||||||
chaiscript::Boxed_Value make_boxed(game::native::VariableValue value);
|
|
||||||
|
|
||||||
static utils::hook start_hook_;
|
static utils::hook start_hook_;
|
||||||
static utils::hook stop_hook_;
|
static utils::hook stop_hook_;
|
||||||
@ -110,19 +29,4 @@ private:
|
|||||||
|
|
||||||
static void start_execution();
|
static void start_execution();
|
||||||
static void stop_execution();
|
static void stop_execution();
|
||||||
|
|
||||||
int get_field_id(int classnum, const std::string& field) const;
|
|
||||||
void set_entity_field(const std::string& field, unsigned int entity_id, const chaiscript::Boxed_Value& value);
|
|
||||||
chaiscript::Boxed_Value get_entity_field(const std::string& field, unsigned int entity_id);
|
|
||||||
|
|
||||||
static bool set_entity_field_safe(game::native::scr_entref_t entref, int offset);
|
|
||||||
static bool get_entity_field_safe(game::native::scr_entref_t entref, int offset, game::native::VariableValue* value);
|
|
||||||
|
|
||||||
void notify(const std::string& event, unsigned int entity_id, std::vector<chaiscript::Boxed_Value> arguments) const;
|
|
||||||
|
|
||||||
void push_param(const chaiscript::Boxed_Value& value) const;
|
|
||||||
chaiscript::Boxed_Value get_return_value();
|
|
||||||
chaiscript::Boxed_Value call(const std::string& function, unsigned int entity_id, std::vector<chaiscript::Boxed_Value> arguments);
|
|
||||||
static bool call_safe(game::native::scr_call_t function, game::native::scr_entref_t entref);
|
|
||||||
static int find_function_index(const std::string& function, bool prefer_global);
|
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user