Make LoadInteriorRefList async without crash using SKSE Task interface
This commit is contained in:
@@ -36,12 +36,12 @@ int CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_k
|
|||||||
const RE::TESBoundObject * base = ref->GetBaseObject();
|
const RE::TESBoundObject * base = ref->GetBaseObject();
|
||||||
if (base) {
|
if (base) {
|
||||||
RE::FormID base_form_id = base->GetFormID();
|
RE::FormID base_form_id = base->GetFormID();
|
||||||
if (base_form_id == 7) {
|
const RE::FormType form_type = base->GetFormType();
|
||||||
// skip saving player ref
|
if (base_form_id == 0x7 || form_type == RE::FormType::ActorCharacter) {
|
||||||
|
// skip saving player ref or other companions
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const char * form_name = base->GetName();
|
const char * form_name = base->GetName();
|
||||||
const RE::FormType form_type = base->GetFormType();
|
|
||||||
logger::info(FMT_STRING("CreateInteriorRefList form: {} ({:x})"), form_name, (uint32_t)form_type);
|
logger::info(FMT_STRING("CreateInteriorRefList form: {} ({:x})"), form_name, (uint32_t)form_type);
|
||||||
float position_x = ref->GetPositionX();
|
float position_x = ref->GetPositionX();
|
||||||
float position_y = ref->GetPositionY();
|
float position_y = ref->GetPositionY();
|
||||||
@@ -131,7 +131,7 @@ bool ClearCell(RE::StaticFunctionTag*)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Destroy existing references
|
// Destroy existing references
|
||||||
for (auto entry = cell->references.begin(); entry != cell->references.end(); ++entry)
|
for (auto entry = cell->references.begin(); entry != cell->references.end();)
|
||||||
{
|
{
|
||||||
RE::TESObjectREFR * ref = (*entry).get();
|
RE::TESObjectREFR * ref = (*entry).get();
|
||||||
RE::FormID form_id = ref->GetFormID();
|
RE::FormID form_id = ref->GetFormID();
|
||||||
@@ -152,11 +152,13 @@ bool ClearCell(RE::StaticFunctionTag*)
|
|||||||
} else {
|
} else {
|
||||||
logger::info(FMT_STRING("ClearCell ref not in ANY file! {:x} {}"), (uint32_t)form_id, ref->GetName());
|
logger::info(FMT_STRING("ClearCell ref not in ANY file! {:x} {}"), (uint32_t)form_id, ref->GetName());
|
||||||
}
|
}
|
||||||
|
++entry;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger::info(FMT_STRING("ClearCell ref is a temp ref, deleting {:x} {}"), (uint32_t)form_id, ref->GetName());
|
logger::info(FMT_STRING("ClearCell ref is a temp ref, deleting {:x} {}"), (uint32_t)form_id, ref->GetName());
|
||||||
ref->Disable(); // disabling first is required to prevent CTD on unloading cell
|
ref->Disable(); // disabling first is required to prevent CTD on unloading cell
|
||||||
ref->SetDelete(true);
|
ref->SetDelete(true);
|
||||||
|
cell->references.erase(*entry++); // prevents slowdowns after many runs of ClearCell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,9 +212,6 @@ bool LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_ke
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SKSE::RegistrationMap<bool> regMap = SKSE::RegistrationMap<bool>();
|
|
||||||
regMap.Register(quest, RE::BSFixedString("OnLoadInteriorRefList"));
|
|
||||||
|
|
||||||
RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton();
|
RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton();
|
||||||
RE::BSScript::Internal::VirtualMachine * a_vm = RE::BSScript::Internal::VirtualMachine::GetSingleton();
|
RE::BSScript::Internal::VirtualMachine * a_vm = RE::BSScript::Internal::VirtualMachine::GetSingleton();
|
||||||
using func_t = decltype(&PlaceAtMe);
|
using func_t = decltype(&PlaceAtMe);
|
||||||
@@ -234,14 +233,19 @@ bool LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_ke
|
|||||||
logger::info(FMT_STRING("LoadInteriorRefList lookup cell override name: {} id: {:x}"), cell->GetName(), (uint32_t)cell->GetFormID());
|
logger::info(FMT_STRING("LoadInteriorRefList lookup cell override name: {} id: {:x}"), cell->GetName(), (uint32_t)cell->GetFormID());
|
||||||
if (!cell) {
|
if (!cell) {
|
||||||
logger::error("LoadInteriorRefList cell is null!");
|
logger::error("LoadInteriorRefList cell is null!");
|
||||||
regMap.SendEvent(false);
|
|
||||||
regMap.Unregister(quest);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//RE::TESObjectREFR * x_marker = data_handler->LookupForm<RE::TESObjectREFR>(6628, "BazaarRealm.esp");
|
//RE::TESObjectREFR * x_marker = data_handler->LookupForm<RE::TESObjectREFR>(6628, "BazaarRealm.esp");
|
||||||
if (target_ref) {
|
if (target_ref) {
|
||||||
FFIResult<RefRecordVec> result = get_interior_ref_list(api_url.c_str(), api_key.c_str(), interior_ref_list_id);
|
FFIResult<RefRecordVec> result = get_interior_ref_list(api_url.c_str(), api_key.c_str(), interior_ref_list_id);
|
||||||
|
|
||||||
|
// Placing the refs must be done on the main thread otherwise calling MoveTo causes a crash
|
||||||
|
auto task = SKSE::GetTaskInterface();
|
||||||
|
task->AddTask([result, target_ref, quest, cell, data_handler, a_vm, MoveTo_Native, PlaceAtMe_Native]() {
|
||||||
|
SKSE::RegistrationMap<bool> regMap = SKSE::RegistrationMap<bool>();
|
||||||
|
regMap.Register(quest, RE::BSFixedString("OnLoadInteriorRefList"));
|
||||||
|
|
||||||
if (result.IsOk()) {
|
if (result.IsOk()) {
|
||||||
logger::info("LoadInteriorRefList get_interior_ref_list result: OK");
|
logger::info("LoadInteriorRefList get_interior_ref_list result: OK");
|
||||||
RefRecordVec vec = result.AsOk();
|
RefRecordVec vec = result.AsOk();
|
||||||
@@ -251,7 +255,7 @@ bool LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_ke
|
|||||||
RefRecord ref = vec.ptr[i];
|
RefRecord ref = vec.ptr[i];
|
||||||
logger::info(FMT_STRING("LoadInteriorRefList ref base_mod_name: {}, base_local_form_id: {:x}"), ref.base_mod_name, ref.base_local_form_id);
|
logger::info(FMT_STRING("LoadInteriorRefList ref base_mod_name: {}, base_local_form_id: {:x}"), ref.base_mod_name, ref.base_local_form_id);
|
||||||
logger::info(FMT_STRING("LoadInteriorRefList ref position {:.2f} {:.2f} {:.2f}, angle: {:.2f} {:.2f} {:.2f}, scale: {:d}"), ref.position_x, ref.position_y, ref.position_z, ref.angle_x, ref.angle_y, ref.angle_z, ref.scale);
|
logger::info(FMT_STRING("LoadInteriorRefList ref position {:.2f} {:.2f} {:.2f}, angle: {:.2f} {:.2f} {:.2f}, scale: {:d}"), ref.position_x, ref.position_y, ref.position_z, ref.angle_x, ref.angle_y, ref.angle_z, ref.scale);
|
||||||
if (ref.base_local_form_id == 7) {
|
if (strcmp(ref.base_mod_name, "Skyrim.esm") == 0 && ref.base_local_form_id == 7) {
|
||||||
logger::info("LoadInteriorRefList skipping player ref");
|
logger::info("LoadInteriorRefList skipping player ref");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -297,9 +301,7 @@ bool LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_ke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RE::ObjectRefHandle handle = game_ref->CreateRefHandle();
|
MoveTo_Native(game_ref, game_ref->CreateRefHandle(), cell, cell->worldSpace, position, angle);
|
||||||
logger::info(FMT_STRING("LoadInteriorRefList ref handle: {:x}, game_ref: {:x}"), (uint32_t)handle.get().get(), (uint32_t)game_ref);
|
|
||||||
MoveTo_Native(game_ref, handle, cell, cell->worldSpace, position, angle);
|
|
||||||
game_ref->data.angle = angle; // set angle directly to fix bug with MoveTo in an unloaded target cell
|
game_ref->data.angle = angle; // set angle directly to fix bug with MoveTo in an unloaded target cell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,11 +313,12 @@ bool LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_ke
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
regMap.SendEvent(true);
|
||||||
|
regMap.Unregister(quest);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
logger::error("LoadInteriorRefList target_ref is null!");
|
logger::error("LoadInteriorRefList target_ref is null!");
|
||||||
regMap.SendEvent(false);
|
|
||||||
regMap.Unregister(quest);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,8 +330,6 @@ bool LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_ke
|
|||||||
//RE::TESObjectREFR * new_ref = func(RE::BSScript::Internal::VirtualMachine::GetSingleton(), 1, player, gold, 50, false, false);
|
//RE::TESObjectREFR * new_ref = func(RE::BSScript::Internal::VirtualMachine::GetSingleton(), 1, player, gold, 50, false, false);
|
||||||
//_MESSAGE("New ref initially disabled: %d", new_ref->IsDisabled());
|
//_MESSAGE("New ref initially disabled: %d", new_ref->IsDisabled());
|
||||||
//_MESSAGE("New ref persistent: %d", new_ref->loadedData->flags & RE::TESObjectREFR::RecordFlags::kPersistent);
|
//_MESSAGE("New ref persistent: %d", new_ref->loadedData->flags & RE::TESObjectREFR::RecordFlags::kPersistent);
|
||||||
regMap.SendEvent(true);
|
|
||||||
regMap.Unregister(quest);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,9 +345,9 @@ bool LoadInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadInteriorRefListImpl(api_url, api_key, interior_ref_list_id, target_ref, quest);
|
// LoadInteriorRefListImpl(api_url, api_key, interior_ref_list_id, target_ref, quest);
|
||||||
// TODO: making this async causes a crash
|
// TODO: making this async causes a crash
|
||||||
// std::thread thread(LoadInteriorRefListImpl, api_url, api_key, interior_ref_list_id, target_ref, quest);
|
std::thread thread(LoadInteriorRefListImpl, api_url, api_key, interior_ref_list_id, target_ref, quest);
|
||||||
// thread.detach();
|
thread.detach();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user