Working variable fields

This commit is contained in:
momo5502 2019-01-19 19:29:35 +01:00
parent 9d83fc2766
commit 5c2e089404
5 changed files with 332 additions and 90 deletions

View File

@ -47,6 +47,8 @@ namespace game
int* g_script_error_level; int* g_script_error_level;
jmp_buf* g_script_error; jmp_buf* g_script_error;
scr_classStruct_t* g_classMap;
void AddRefToValue(VariableValue* value) void AddRefToValue(VariableValue* value)
{ {
if (value->type == SCRIPT_OBJECT) if (value->type == SCRIPT_OBJECT)
@ -69,15 +71,42 @@ namespace game
} }
} }
__declspec(naked) unsigned int find_variable_dedicated(unsigned int parentId, unsigned int name)
{
static DWORD func = 0x4E7ED0;
__asm
{
mov eax, name
mov ecx, parentId
call func
retn
}
}
unsigned int FindVariable(const unsigned int parentId, const unsigned int name)
{
if (is_dedi())
{
return find_variable_dedicated(parentId, name);
}
else
{
return reinterpret_cast<unsigned int(*)(unsigned int, unsigned int)> //
(SELECT_VALUE(0x4C4E70, 0x5651F0, 0x0))(parentId, name);
}
}
__declspec(naked) VariableValue get_entity_field_value_dedicated(unsigned int classnum, int entnum, int _offset) __declspec(naked) VariableValue get_entity_field_value_dedicated(unsigned int classnum, int entnum, int _offset)
{ {
static DWORD func = 0x4F1400;
__asm __asm
{ {
push _offset push _offset
push entnum push entnum
mov ecx, classnum mov ecx, classnum
mov eax, 4F1400h call func
call eax
add esp, 8h add esp, 8h
retn retn
} }
@ -150,6 +179,34 @@ namespace game
} }
} }
__declspec(naked) int scr_set_object_field_dedicated(unsigned int classnum, int entnum, int _offset)
{
static DWORD func = 0x4B15C0;
__asm
{
mov ecx, _offset
mov eax, entnum
push classnum
call func
add esp, 4h
retn
}
}
int Scr_SetObjectField(const unsigned int classnum, const int entnum, const int offset)
{
if (is_dedi())
{
return scr_set_object_field_dedicated(classnum, entnum, offset);
}
else
{
return reinterpret_cast<int(*)(unsigned int, int, int)> //
(SELECT_VALUE(0x42CAD0, 0x52BCC0, 0x0))(classnum, entnum, offset);
}
}
const char* SL_ConvertToString(const unsigned int stringValue) const char* SL_ConvertToString(const unsigned int stringValue)
{ {
if (!stringValue) return nullptr; if (!stringValue) return nullptr;
@ -228,6 +285,8 @@ namespace game
native::g_script_error_level = reinterpret_cast<int*>(SELECT_VALUE(0x1BEFCFC, 0x20B21FC, 0x1F5B058)); native::g_script_error_level = reinterpret_cast<int*>(SELECT_VALUE(0x1BEFCFC, 0x20B21FC, 0x1F5B058));
native::g_script_error = reinterpret_cast<jmp_buf*>(SELECT_VALUE(0x1BF1D18, 0x20B4218, 0x1F5A818)); native::g_script_error = reinterpret_cast<jmp_buf*>(SELECT_VALUE(0x1BF1D18, 0x20B4218, 0x1F5A818));
native::g_classMap = reinterpret_cast<native::scr_classStruct_t*>(SELECT_VALUE(0x92D140, 0x8B4300, 0x7C0408));
native::levelEntityId = reinterpret_cast<unsigned int*>(SELECT_VALUE(0x1BCBCA4, 0x208E1A4, 0x1CD873C)); native::levelEntityId = reinterpret_cast<unsigned int*>(SELECT_VALUE(0x1BCBCA4, 0x208E1A4, 0x1CD873C));
} }
} }

View File

@ -62,8 +62,12 @@ namespace game
extern int* g_script_error_level; extern int* g_script_error_level;
extern jmp_buf* g_script_error; extern jmp_buf* g_script_error;
extern scr_classStruct_t* g_classMap;
void AddRefToValue(VariableValue* value); void AddRefToValue(VariableValue* value);
unsigned int FindVariable(unsigned int parentId, unsigned int name);
VariableValue GetEntityFieldValue(unsigned int classnum, int entnum, int offset); VariableValue GetEntityFieldValue(unsigned int classnum, int entnum, int offset);
void* MT_Alloc(int numBytes, int type); void* MT_Alloc(int numBytes, int type);
@ -72,6 +76,7 @@ namespace game
void Scr_ClearOutParams(); void Scr_ClearOutParams();
scr_entref_t Scr_GetEntityIdRef(unsigned int id); scr_entref_t Scr_GetEntityIdRef(unsigned int id);
scr_call_t Scr_GetFunc(unsigned int index); scr_call_t Scr_GetFunc(unsigned int index);
int Scr_SetObjectField(unsigned int classnum, int entnum, int offset);
const char* SL_ConvertToString(unsigned int stringValue); const char* SL_ConvertToString(unsigned int stringValue);
unsigned int SL_GetString(const char* str, unsigned int user); unsigned int SL_GetString(const char* str, unsigned int user);

View File

@ -412,7 +412,7 @@ namespace game
scr_entref_raw raw; scr_entref_raw raw;
}; };
typedef void(__cdecl * scr_call_t)(int entref); typedef void (__cdecl * scr_call_t)(int entref);
enum scriptType_e enum scriptType_e
{ {
@ -422,7 +422,8 @@ namespace game
SCRIPT_VECTOR = 4, SCRIPT_VECTOR = 4,
SCRIPT_FLOAT = 5, SCRIPT_FLOAT = 5,
SCRIPT_INTEGER = 6, SCRIPT_INTEGER = 6,
SCRIPT_END = 8, // Custom SCRIPT_END = 8,
// Custom
}; };
struct VariableStackBuffer struct VariableStackBuffer
@ -455,11 +456,11 @@ namespace game
struct function_stack_t struct function_stack_t
{ {
const char *pos; const char* pos;
unsigned int localId; unsigned int localId;
unsigned int localVarCount; unsigned int localVarCount;
VariableValue *top; VariableValue* top;
VariableValue *startTop; VariableValue* startTop;
}; };
struct function_frame_t struct function_frame_t
@ -470,11 +471,11 @@ namespace game
struct scrVmPub_t struct scrVmPub_t
{ {
unsigned int *localVars; unsigned int* localVars;
VariableValue *maxstack; VariableValue* maxstack;
int function_count; int function_count;
function_frame_t *function_frame; function_frame_t* function_frame;
VariableValue *top; VariableValue* top;
/*bool debugCode; /*bool debugCode;
bool abort_on_error; bool abort_on_error;
bool terminal_error; bool terminal_error;
@ -486,5 +487,13 @@ namespace game
function_frame_t function_frame_start[32]; function_frame_t function_frame_start[32];
VariableValue stack[2048]; VariableValue stack[2048];
}; };
struct scr_classStruct_t
{
unsigned __int16 id;
unsigned __int16 entArrayId;
char charId;
const char* name;
};
} }
} }

View File

@ -75,6 +75,16 @@ void scripting::entity::notify(const std::string& event, const std::vector<chais
this->environment_->notify(event, this->get_entity_id(), arguments); this->environment_->notify(event, this->get_entity_id(), arguments);
} }
void scripting::entity::set(const std::string& field, const chaiscript::Boxed_Value& value)
{
this->environment_->set_entity_field(field, this->get_entity_id(), value);
}
chaiscript::Boxed_Value scripting::entity::get(const std::string& field)
{
return this->environment_->get_entity_field(field, this->get_entity_id());
}
scripting::variable::variable(game::native::VariableValue value) : value_(value) scripting::variable::variable(game::native::VariableValue value) : value_(value)
{ {
game::native::AddRefToValue(&value); game::native::AddRefToValue(&value);
@ -90,6 +100,28 @@ scripting::variable::operator game::native::VariableValue() const
return this->value_; return this->value_;
} }
scripting::stack_context::stack_context()
{
this->inparamcount_ = game::native::scr_VmPub->inparamcount;
this->outparamcount_ = game::native::scr_VmPub->outparamcount;
this->top_ = game::native::scr_VmPub->top;
this->maxstack_ = 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->inparamcount_;
game::native::scr_VmPub->outparamcount = this->outparamcount_;
game::native::scr_VmPub->top = this->top_;
game::native::scr_VmPub->maxstack = this->maxstack_;
}
void scripting::post_start() void scripting::post_start()
{ {
on_start([this]() on_start([this]()
@ -157,6 +189,8 @@ void scripting::initialize()
this->load_scripts(); this->load_scripts();
notification::listen([this](notification::event* event) notification::listen([this](notification::event* event)
{
try
{ {
std::vector<chaiscript::Boxed_Value> arguments; std::vector<chaiscript::Boxed_Value> arguments;
@ -166,8 +200,6 @@ void scripting::initialize()
} }
for (auto listener = this->event_listeners_.begin(); listener.is_valid(); ++listener) for (auto listener = this->event_listeners_.begin(); listener.is_valid(); ++listener)
{
try
{ {
if (listener->event == event->name && listener->entity_id == event->entity_id) if (listener->event == event->name && listener->entity_id == event->entity_id)
{ {
@ -179,11 +211,24 @@ void scripting::initialize()
listener->callback(arguments); 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) catch (chaiscript::exception::eval_error& e)
{ {
throw std::runtime_error(e.pretty_print()); throw std::runtime_error(e.pretty_print());
} }
}
}); });
} }
@ -193,6 +238,9 @@ void scripting::initialize_entity()
this->chai_->add(chaiscript::constructor<entity()>(), "entity"); this->chai_->add(chaiscript::constructor<entity()>(), "entity");
this->chai_->add(chaiscript::constructor<entity(const 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(&entity::on_notify), "onNotify");
this->chai_->add(chaiscript::fun([](const entity& ent, const std::string& event, this->chai_->add(chaiscript::fun([](const entity& ent, const std::string& event,
const std::function<void(const std::vector<chaiscript::Boxed_Value>&)>& const std::function<void(const std::vector<chaiscript::Boxed_Value>&)>&
@ -206,6 +254,32 @@ void scripting::initialize_entity()
return lhs = rhs; 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 // Notification
this->chai_->add(chaiscript::fun(&entity::notify), "vectorNotify"); this->chai_->add(chaiscript::fun(&entity::notify), "vectorNotify");
this->chai_->add(chaiscript::fun([](const entity& ent, const std::string& event) this->chai_->add(chaiscript::fun([](const entity& ent, const std::string& event)
@ -469,45 +543,92 @@ void scripting::stop_execution()
} }
} }
int scripting::get_field_id(const int classnum, const std::string& field) const int scripting::get_field_id(const int classnum, const std::string& field)
{ {
switch (classnum) 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]()
{ {
case 0: // Entity game::native::VariableUnion u{};
case 1: // HudElem u.stringValue = field_str;
case 2: // Pathnode game::native::RemoveRefToValue(game::native::SCRIPT_STRING, u);
case 3: // VehPathNode });
case 4: // VehTrackSegment
case 6: // PIPElem
default: const auto offset = game::native::FindVariable(class_id, field_str);
return -1; 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, void scripting::notify(const std::string& event, const unsigned int entity_id,
std::vector<chaiscript::Boxed_Value> arguments) std::vector<chaiscript::Boxed_Value> arguments)
{ {
stack_context _;
const auto old_args = game::native::scr_VmPub->inparamcount;
const auto old_params = game::native::scr_VmPub->outparamcount;
const auto old_stack_top = game::native::scr_VmPub->top;
const auto old_stack_end = game::native::scr_VmPub->maxstack;
game::native::VariableValue stack[512];
game::native::scr_VmPub->top = stack;
game::native::scr_VmPub->maxstack = &stack[ARRAYSIZE(stack) - 1];
game::native::scr_VmPub->inparamcount = 0;
game::native::scr_VmPub->outparamcount = 0;
const auto cleanup = gsl::finally([=]()
{
game::native::Scr_ClearOutParams();
game::native::scr_VmPub->inparamcount = old_args;
game::native::scr_VmPub->outparamcount = old_params;
game::native::scr_VmPub->top = old_stack_top;
game::native::scr_VmPub->maxstack = old_stack_end;
});
std::reverse(arguments.begin(), arguments.end()); std::reverse(arguments.begin(), arguments.end());
for (const auto& argument : arguments) for (const auto& argument : arguments)
@ -534,25 +655,7 @@ chaiscript::Boxed_Value scripting::call(const std::string& function, const unsig
const auto function_ptr = game::native::Scr_GetFunc(function_index); const auto function_ptr = game::native::Scr_GetFunc(function_index);
const auto old_args = game::native::scr_VmPub->inparamcount; stack_context _;
const auto old_params = game::native::scr_VmPub->outparamcount;
const auto old_stack_top = game::native::scr_VmPub->top;
const auto old_stack_end = game::native::scr_VmPub->maxstack;
game::native::VariableValue stack[512];
game::native::scr_VmPub->top = stack;
game::native::scr_VmPub->maxstack = &stack[ARRAYSIZE(stack) - 1];
game::native::scr_VmPub->inparamcount = 0;
game::native::scr_VmPub->outparamcount = 0;
const auto cleanup = gsl::finally([=]()
{
game::native::Scr_ClearOutParams();
game::native::scr_VmPub->inparamcount = old_args;
game::native::scr_VmPub->outparamcount = old_params;
game::native::scr_VmPub->top = old_stack_top;
game::native::scr_VmPub->maxstack = old_stack_end;
});
std::reverse(arguments.begin(), arguments.end()); std::reverse(arguments.begin(), arguments.end());
for (const auto& argument : arguments) for (const auto& argument : arguments)
@ -589,6 +692,38 @@ bool scripting::call_safe(const game::native::scr_call_t function, const game::n
*game::native::g_script_error_level -= 1; *game::native::g_script_error_level -= 1;
return true; 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) #pragma warning(pop)
int scripting::find_function_index(const std::string& function, const bool prefer_global) int scripting::find_function_index(const std::string& function, const bool prefer_global)
@ -682,7 +817,7 @@ void scripting::push_param(const chaiscript::Boxed_Value& value) const
throw std::runtime_error("Invalid vector length. Size must be exactly 3"); throw std::runtime_error("Invalid vector length. Size must be exactly 3");
} }
const auto unbox_float = [&real_value, this](size_t index) -> float const auto unbox_float = [&real_value, this](const size_t index) -> float
{ {
const auto value = real_value[index]; const auto value = real_value[index];
if (value.get_type_info() == typeid(float)) if (value.get_type_info() == typeid(float))

View File

@ -25,6 +25,9 @@ public:
chaiscript::Boxed_Value call(const std::string& function, const std::vector<chaiscript::Boxed_Value>& arguments) 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 notify(const std::string& event, const std::vector<chaiscript::Boxed_Value>& arguments) const;
void set(const std::string& field, const chaiscript::Boxed_Value& value);
chaiscript::Boxed_Value get(const std::string& field);
private: private:
scripting* environment_; scripting* environment_;
unsigned int entity_id_; unsigned int entity_id_;
@ -51,6 +54,14 @@ public:
bool is_volatile = false; 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;
@ -61,8 +72,26 @@ public:
static void propagate_error(const std::exception& e); static void propagate_error(const std::exception& e);
private: private:
class stack_context final
{
public:
stack_context();
~stack_context();
private:
game::native::VariableValue stack_[512]{};
game::native::VariableValue *maxstack_;
game::native::VariableValue *top_;
unsigned int inparamcount_;
unsigned int outparamcount_;
};
std::unique_ptr<chaiscript::ChaiScript> chai_; std::unique_ptr<chaiscript::ChaiScript> chai_;
utils::chain<event_listener> event_listeners_; 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 add_event_listener(const event_listener& listener);
@ -82,7 +111,12 @@ 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; int get_field_id(int classnum, const std::string& field);
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); void notify(const std::string& event, unsigned int entity_id, std::vector<chaiscript::Boxed_Value> arguments);