Skip to content

Commit 06c8e3e

Browse files
authored
Lua Fix NullReferenceException exception when invoking valtype instance method (#1747)
* Lua Fix System.NullReferenceException exception when invoking valtype instance method * Copy back changes to our local valuetype storage after invoke
1 parent a2025ed commit 06c8e3e

1 file changed

Lines changed: 21 additions & 4 deletions

File tree

src/mods/bindings/Sdk.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -370,15 +370,15 @@ struct ValueType {
370370
: type(t)
371371
{
372372
if (type != nullptr) {
373-
data.resize(type->get_size());
373+
data.resize(type->get_valuetype_size());
374374
}
375375
}
376376

377377
ValueType(::sdk::RETypeDefinition* t, void* addr): type(t) {
378378
if (t != nullptr && addr != nullptr) {
379379
uint8_t* raw_data = reinterpret_cast<uint8_t*>(addr);
380-
data.reserve(t->get_size());
381-
data.insert(data.begin(), raw_data, raw_data + t->get_size());
380+
data.reserve(t->get_valuetype_size());
381+
data.insert(data.begin(), raw_data, raw_data + t->get_valuetype_size());
382382
}
383383
}
384384

@@ -425,16 +425,33 @@ struct ValueType {
425425
return sol::make_object(l, sol::nil);
426426
}
427427

428-
auto real_obj = (void*)address();
429428
auto def = type->get_method(name);
430429

431430
if (def == nullptr) {
432431
return sol::make_object(l, sol::nil);
433432
}
434433

434+
// For instance methods on value types, we need to construct a fake boxed object.
435+
// The native invoke() expects a ManagedObject* with a 0x10 byte header:
436+
// 0x00: REObjectInfo* (type info / vtable pointer)
437+
// 0x08: uint32_t refcount + padding
438+
// 0x10: actual value type data starts here
439+
std::vector<uint8_t> fake_boxed_storage(0x10 + type->get_valuetype_size(), 0);
440+
// REObject header: REObjectInfo* at offset 0x00
441+
*(void**)&fake_boxed_storage[0x00] = (void*)type;
442+
// REManagedObject: reference count at offset 0x08 (set high to prevent GC interference)
443+
*(uint32_t*)&fake_boxed_storage[0x08] = 9999;
444+
// Copy value type data at offset 0x10
445+
memcpy(&fake_boxed_storage[0x10], data.data(), type->get_valuetype_size());
446+
447+
auto real_obj = (void*)fake_boxed_storage.data();
448+
435449
auto vec_args = ::api::sdk::build_args(va);
436450
auto ret_val = def->invoke(real_obj, std::span(vec_args));
437451

452+
// copy back any changes to the value type from the fake boxed storage
453+
memcpy(data.data(), &fake_boxed_storage[0x10], type->get_valuetype_size());
454+
438455
if (ret_val.exception_thrown) {
439456
throw sol::error("Invoke threw an exception");
440457
}

0 commit comments

Comments
 (0)