Refactor API calls to use FFIResult

Also create a `successReg` and `failReg` for every async function. Report error string to `failReg`.
This commit is contained in:
Tyler Hallada 2020-10-18 20:53:41 -04:00
parent c42b29af5d
commit 055905bffc
5 changed files with 241 additions and 235 deletions

View File

@ -1,45 +1,50 @@
#include "bindings.h"
bool Init(RE::StaticFunctionTag*)
{
bool Init(RE::StaticFunctionTag*) {
logger::info("Entered Init");
bool result = init();
if (result) {
logger::info("Init successful");
return true;
}
else {
} else {
logger::error("Init failed");
return false;
}
}
std::string GenerateApiKey(RE::StaticFunctionTag*)
{
std::string GenerateApiKey(RE::StaticFunctionTag*) {
logger::info("Entered GenerateApiKey");
char *api_key = generate_api_key();
logger::info(FMT_STRING("GenerateApiKey api_key: {}"), api_key);
return api_key;
}
bool StatusCheckImpl(RE::BSFixedString api_url, RE::TESQuest* quest)
{
void StatusCheckImpl(RE::BSFixedString api_url, RE::TESQuest* quest) {
logger::info("Entered StatusCheckImpl");
if (!quest) {
logger::error("StatusCheck quest is null!");
return false;
return;
}
SKSE::RegistrationMap<bool> regMap = SKSE::RegistrationMap<bool>();
regMap.Register(quest, RE::BSFixedString("OnStatusCheck"));
SKSE::RegistrationMap<bool> successReg = SKSE::RegistrationMap<bool>();
successReg.Register(quest, RE::BSFixedString("OnStatusCheckSuccess"));
SKSE::RegistrationMap<RE::BSFixedString> failReg = SKSE::RegistrationMap<RE::BSFixedString>();
failReg.Register(quest, RE::BSFixedString("OnStatusCheckFail"));
logger::info(FMT_STRING("StatusCheck api_url: {}"), api_url);
bool result = status_check(api_url.c_str());
logger::info(FMT_STRING("StatusCheck result: {}"), result ? "true" : "false");
regMap.SendEvent(result);
regMap.Unregister(quest);
return result;
FFIResult<bool> result = status_check(api_url.c_str());
if (result.IsOk()) {
bool success = result.AsOk();
logger::info(FMT_STRING("StatusCheck success: {}"), success);
successReg.SendEvent(success);
} else {
const char* error = result.AsErr();
logger::error(FMT_STRING("StatusCheck failure: {}"), error);
failReg.SendEvent(RE::BSFixedString(error));
}
successReg.Unregister(quest);
failReg.Unregister(quest);
return;
}
bool StatusCheck(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::TESQuest* quest) {

View File

@ -2,31 +2,32 @@
#include "NativeFunctions.h"
int CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESQuest* quest)
{
void CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESQuest* quest) {
logger::info("Entered CreateInteriorRefListImpl");
if (!quest) {
logger::error("LoadInteriorRefList quest is null!");
return -1;
logger::error("CreateInteriorRefListImpl quest is null!");
return;
}
SKSE::RegistrationMap<int> regMap = SKSE::RegistrationMap<int>();
regMap.Register(quest, RE::BSFixedString("OnCreateInteriorRefList"));
SKSE::RegistrationMap<int> successReg = SKSE::RegistrationMap<int>();
successReg.Register(quest, RE::BSFixedString("OnCreateInteriorRefListSuccess"));
SKSE::RegistrationMap<RE::BSFixedString> failReg = SKSE::RegistrationMap<RE::BSFixedString>();
failReg.Register(quest, RE::BSFixedString("OnCreateInteriorRefListFail"));
RE::TESObjectCELL * cell = RE::TESObjectCELL::LookupByEditorID<RE::TESObjectCELL>("BREmpty");
logger::info(FMT_STRING("ClearCell lookup cell override name: {} id: {:x}"), cell->GetName(), (uint32_t)cell->GetFormID());
logger::info(FMT_STRING("CreateInteriorRefListImpl lookup cell override name: {} id: {:x}"), cell->GetName(), (uint32_t)cell->GetFormID());
if (!cell) {
logger::error("ClearCell cell is null!");
regMap.SendEvent(-1);
regMap.Unregister(quest);
return -1;
logger::error("CreateInteriorRefListImpl cell is null!");
failReg.SendEvent("Could not find Cell with the editor ID: BREmpty");
successReg.Unregister(quest);
failReg.Unregister(quest);
return;
}
RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton();
std::vector<RefRecord> ref_records;
for (auto entry = cell->references.begin(); entry != cell->references.end(); ++entry)
{
std::vector<RawInteriorRef> raw_interior_refs;
for (auto entry = cell->references.begin(); entry != cell->references.end(); ++entry) {
RE::TESObjectREFR * ref = (*entry).get();
const char * name = ref->GetName();
logger::info(FMT_STRING("CreateInteriorRefList ref: {}"), name);
@ -70,14 +71,13 @@ int CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_k
ref_mod_index = (ref_form_id >> 12) & 0xfff;
const RE::TESFile * ref_file = data_handler->LookupLoadedLightModByIndex(ref_mod_index);
ref_file_name = _strdup(ref_file->fileName);
}
else {
} else {
const RE::TESFile * ref_file = data_handler->LookupLoadedModByIndex(ref_mod_index);
ref_file_name = _strdup(ref_file->fileName);
}
}
ref_records.push_back({
raw_interior_refs.push_back({
base_file_name,
base_local_form_id,
ref_file_name,
@ -93,18 +93,25 @@ int CreateInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_k
}
}
int interior_ref_list_id = create_interior_ref_list(api_url.c_str(), api_key.c_str(), shop_id, &ref_records[0], ref_records.size());
logger::info(FMT_STRING("CreateInteriorRefList result: {}"), interior_ref_list_id);
regMap.SendEvent(interior_ref_list_id);
regMap.Unregister(quest);
return interior_ref_list_id;
FFIResult<int32_t> result = create_interior_ref_list(api_url.c_str(), api_key.c_str(), shop_id, &raw_interior_refs[0], raw_interior_refs.size());
if (result.IsOk()) {
int32_t interior_ref_list_id = result.AsOk();
logger::info(FMT_STRING("CreateInteriorRefList success: {}"), interior_ref_list_id);
successReg.SendEvent(interior_ref_list_id);
} else {
const char* error = result.AsErr();
logger::error(FMT_STRING("CreateInteriorRefList failure: {}"), error);
failReg.SendEvent(RE::BSFixedString(error));
}
successReg.Unregister(quest);
failReg.Unregister(quest);
}
bool CreateInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESQuest* quest) {
logger::info("Entered CreateInteriorRefList");
if (!quest) {
logger::error("LoadInteriorRefList quest is null!");
logger::error("CreateInteriorRefList quest is null!");
return false;
}
@ -113,8 +120,7 @@ bool CreateInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE
return true;
}
bool ClearCell(RE::StaticFunctionTag*)
{
bool ClearCell(RE::StaticFunctionTag*) {
logger::info("Entered ClearCell");
RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton();
@ -127,8 +133,7 @@ bool ClearCell(RE::StaticFunctionTag*)
}
// Destroy existing references
for (auto entry = cell->references.begin(); entry != cell->references.end();)
{
for (auto entry = cell->references.begin(); entry != cell->references.end();) {
RE::TESObjectREFR * ref = (*entry).get();
RE::FormID form_id = ref->GetFormID();
logger::info(FMT_STRING("ClearCell ref form_id: {:x}"), (uint32_t)form_id);
@ -141,16 +146,14 @@ bool ClearCell(RE::StaticFunctionTag*)
uint32_t local_form_id = is_light ? ref->GetFormID() & 0xfff : form_id & 0xFFFFFF;
if (!data_handler->LookupForm<RE::TESObjectREFR>(local_form_id, file->fileName)) {
logger::info(FMT_STRING("ClearCell ref was not in mod file! {:x} {}"), local_form_id, ref->GetName());
}
else {
} else {
logger::info(FMT_STRING("ClearCell ref in mod file {:x} {}"), local_form_id, ref->GetName());
}
} else {
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());
ref->Disable(); // disabling first is required to prevent CTD on unloading cell
ref->SetDelete(true);
@ -161,13 +164,12 @@ bool ClearCell(RE::StaticFunctionTag*)
return true;
}
bool LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t interior_ref_list_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest)
{
void LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t interior_ref_list_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) {
logger::info("Entered LoadInteriorRefListImpl");
if (!quest) {
logger::error("LoadInteriorRefList quest is null!");
return false;
logger::error("LoadInteriorRefListImpl quest is null!");
return;
}
RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton();
@ -178,28 +180,30 @@ bool LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_ke
REL::Relocation<func_t2> MoveTo_Native(RE::Offset::TESObjectREFR::MoveTo);
RE::TESObjectCELL * cell = RE::TESObjectCELL::LookupByEditorID<RE::TESObjectCELL>("BREmpty");
logger::info(FMT_STRING("LoadInteriorRefList lookup cell override name: {} id: {:x}"), cell->GetName(), (uint32_t)cell->GetFormID());
logger::info(FMT_STRING("LoadInteriorRefListImpl lookup cell override name: {} id: {:x}"), cell->GetName(), (uint32_t)cell->GetFormID());
if (!cell) {
logger::error("LoadInteriorRefList cell is null!");
return false;
logger::error("LoadInteriorRefListImpl cell is null!");
return;
}
if (target_ref) {
FFIResult<RefRecordVec> result = get_interior_ref_list(api_url.c_str(), api_key.c_str(), interior_ref_list_id);
FFIResult<RawInteriorRefVec> 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"));
SKSE::RegistrationMap<bool> successReg = SKSE::RegistrationMap<bool>();
successReg.Register(quest, RE::BSFixedString("OnLoadInteriorRefListSuccess"));
SKSE::RegistrationMap<RE::BSFixedString> failReg = SKSE::RegistrationMap<RE::BSFixedString>();
failReg.Register(quest, RE::BSFixedString("OnLoadInteriorRefListFail"));
if (result.IsOk()) {
logger::info("LoadInteriorRefList get_interior_ref_list result: OK");
RefRecordVec vec = result.AsOk();
RawInteriorRefVec vec = result.AsOk();
logger::info(FMT_STRING("LoadInteriorRefList vec len: {:d}, cap: {:d}"), vec.len, vec.cap);
for (int i = 0; i < vec.len; i++) {
RefRecord ref = vec.ptr[i];
RawInteriorRef 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 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 (strcmp(ref.base_mod_name, "Skyrim.esm") == 0 && ref.base_local_form_id == 7) {
@ -214,8 +218,7 @@ bool LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_ke
game_ref = data_handler->LookupForm<RE::TESObjectREFR>(ref.ref_local_form_id, ref.ref_mod_name);
if (game_ref) {
logger::info(FMT_STRING("LoadInteriorRefList lookup ref name: {}, form_id: {:x}"), game_ref->GetName(), (uint32_t)game_ref->GetFormID());
}
else {
} else {
logger::info(FMT_STRING("LoadInteriorRefList lookup ref not found, ref_mod_name: {}, ref_local_form_id: {:x}"), ref.ref_mod_name, ref.ref_local_form_id);
}
}
@ -230,34 +233,33 @@ bool LoadInteriorRefListImpl(RE::BSFixedString api_url, RE::BSFixedString api_ke
game_ref = PlaceAtMe_Native(a_vm, 0, target_ref, form, 1, false, false);
if (!game_ref) {
logger::error("LoadInteriorRefList failed to place new ref in cell!");
regMap.SendEvent(false);
regMap.Unregister(quest);
return false;
failReg.SendEvent("Failed to place a new ref into the cell");
successReg.Unregister(quest);
failReg.Unregister(quest);
return;
}
}
MoveTo_Native(game_ref, game_ref->CreateRefHandle(), cell, cell->worldSpace, position, angle);
game_ref->data.angle = angle; // set angle directly to fix bug with MoveTo in an unloaded target cell
}
}
else {
} else {
const char * error = result.AsErr();
logger::error(FMT_STRING("LoadInteriorRefList get_interior_ref_list error: {}"), error);
regMap.SendEvent(false);
regMap.Unregister(quest);
return false;
failReg.SendEvent(RE::BSFixedString(error));
successReg.Unregister(quest);
failReg.Unregister(quest);
return;
}
regMap.SendEvent(true);
regMap.Unregister(quest);
successReg.SendEvent(true);
successReg.Unregister(quest);
failReg.Unregister(quest);
});
} else {
logger::error("LoadInteriorRefListImpl target_ref is null!");
return;
}
else {
logger::error("LoadInteriorRefList target_ref is null!");
return false;
}
return true;
}
bool LoadInteriorRefList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t interior_ref_list_id, RE::TESObjectREFR* target_ref, RE::TESQuest* quest) {

View File

@ -34,12 +34,10 @@ bool ClearMerchandiseImpl(RE::TESObjectREFR* merchant_chest, RE::TESObjectREFR*
ref->Disable(); // disabling first is required to prevent CTD on unloading cell
ref->SetDelete(true);
cell->references.erase(*entry++); // prevents slowdowns after many runs of ClearMerchandise
}
else {
} else {
++entry;
}
}
else {
} else {
++entry;
}
}
@ -47,21 +45,21 @@ bool ClearMerchandiseImpl(RE::TESObjectREFR* merchant_chest, RE::TESObjectREFR*
if (ref->IsDisabled() && ref->IsMarkedForDeletion() && ref->IsDeleted()) {
logger::info("ClearMerchandise ref is probably an item from old LoadMerchandise, clearing from cell now");
cell->references.erase(*entry++);
}
else {
} else {
logger::info("ClearMerchandise ref has no base, skipping");
++entry;
}
}
}
} else {
logger::error("ClearMerchandise merchant_chest or merchant_shelf is null!");
logger::error("ClearMerchandiseImpl merchant_chest or merchant_shelf is null!");
return false;
}
return true;
}
bool LoadMerchandiseImpl(
void LoadMerchandiseImpl(
RE::BSFixedString api_url,
RE::BSFixedString api_key,
uint32_t merchandise_list_id,
@ -73,8 +71,8 @@ bool LoadMerchandiseImpl(
RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword,
int page)
{
int page
) {
logger::info("Entered LoadMerchandiseImpl");
logger::info(FMT_STRING("LoadMerchandise page: {:d}"), page);
@ -88,58 +86,66 @@ bool LoadMerchandiseImpl(
if (!merchant_shelf) {
logger::error("LoadMerchandise merchant_shelf is null!");
return false;
return;
}
RE::TESObjectCELL * cell = merchant_shelf->GetParentCell();
RE::TESObjectREFR * toggle_ref = merchant_shelf->GetLinkedRef(toggle_keyword);
if (!toggle_ref) {
logger::error("LoadMerchandise toggle_ref is null!");
return false;
return;
}
RE::TESObjectREFR * merchant_chest = merchant_shelf->GetLinkedRef(chest_keyword);
if (!merchant_chest) {
logger::error("LoadMerchandise merchant_chest is null!");
return false;
return;
}
FFIResult<MerchRecordVec> result = get_merchandise_list(api_url.c_str(), api_key.c_str(), merchandise_list_id);
FFIResult<RawMerchandiseVec> result = get_merchandise_list(api_url.c_str(), api_key.c_str(), merchandise_list_id);
// Placing the refs must be done on the main thread otherwise disabling & deleting refs in ClearMerchandiseImpl causes a crash
auto task = SKSE::GetTaskInterface();
task->AddTask([result, merchant_chest, merchant_shelf, placeholder_static, shelf_keyword, item_keyword, prev_keyword, next_keyword, page, toggle_ref, cell, data_handler, a_vm, extra_linked_ref_vtbl, MoveTo_Native, PlaceAtMe_Native]() {
// Since this method is running asyncronously in a thread, set up a callback on the trigger ref that will receive an event with the result
SKSE::RegistrationMap<bool> regMap = SKSE::RegistrationMap<bool>();
regMap.Register(toggle_ref, RE::BSFixedString("OnLoadMerchandise"));
SKSE::RegistrationMap<bool> successReg = SKSE::RegistrationMap<bool>();
successReg.Register(toggle_ref, RE::BSFixedString("OnLoadMerchandiseSuccess"));
SKSE::RegistrationMap<RE::BSFixedString> failReg = SKSE::RegistrationMap<RE::BSFixedString>();
failReg.Register(toggle_ref, RE::BSFixedString("OnLoadMerchandiseFail"));
if (result.IsOk()) {
logger::info("LoadMerchandise get_merchandise_list result OK");
MerchRecordVec vec = result.AsOk();
RawMerchandiseVec vec = result.AsOk();
logger::info(FMT_STRING("LoadMerchandise vec len: {:d}, cap: {:d}"), vec.len, vec.cap);
int max_page = std::ceil((float)(vec.len - 1) / (float)9);
if (vec.len > 0 && page > max_page) {
logger::info(FMT_STRING("LoadMerchandise page {:d} is greater than max_page {:d}, doing nothing"), page, max_page);
regMap.SendEvent(true);
regMap.Unregister(toggle_ref);
return true;
successReg.SendEvent(true);
successReg.Unregister(toggle_ref);
failReg.Unregister(toggle_ref);
return;
}
ClearMerchandiseImpl(merchant_chest, merchant_shelf, placeholder_static, shelf_keyword, item_keyword);
if (!ClearMerchandiseImpl(merchant_chest, merchant_shelf, placeholder_static, shelf_keyword, item_keyword)) {
logger::error("LoadMerchandise ClearMerchandiseImpl returned a fail code");
failReg.SendEvent(RE::BSFixedString("Failed to clear existing merchandise from shelf"));
successReg.Unregister(toggle_ref);
failReg.Unregister(toggle_ref);
return;
}
logger::info(FMT_STRING("LoadMerchandise current shelf page is: {:d}"), merchant_shelf->extraList.GetCount());
for (int i = 0; i < vec.len; i++) {
MerchRecord rec = vec.ptr[i];
RawMerchandise merch = vec.ptr[i];
logger::info(FMT_STRING("LoadMerchandise item: {:d}"), i);
if (i < (page - 1) * 9 || i >= (page - 1) * 9 + 9) {
continue;
}
RE::TESForm * form = data_handler->LookupForm(rec.local_form_id, rec.mod_name);
RE::TESForm * form = data_handler->LookupForm(merch.local_form_id, merch.mod_name);
if (!form) { // form is not found, might be in an uninstalled mod
logger::warn(FMT_STRING("LoadMerchandise not spawning ref for form that could not be found in installed mods: {} {:d}"), rec.mod_name, rec.local_form_id);
logger::warn(FMT_STRING("LoadMerchandise not spawning ref for form that could not be found in installed mods: {} {:d}"), merch.mod_name, merch.local_form_id);
continue;
}
logger::info(FMT_STRING("LoadMerchandise lookup form name: {}, form_id: {:x}, form_type: {:x}"), form->GetName(), (uint32_t)form->GetFormID(), (uint32_t)form->GetFormType());
@ -165,7 +171,7 @@ bool LoadMerchandiseImpl(
// This extra count stored on the placeholder_ref indicates the quanity of the merchandise item it is linked to
RE::ExtraCount * extra_page_num = (RE::ExtraCount*)RE::BSExtraData::Create(sizeof(RE::ExtraCount), RE::Offset::ExtraCount::Vtbl.address());
extra_page_num->count = rec.quantity;
extra_page_num->count = merch.quantity;
placeholder_ref->extraList.Add(extra_page_num);
float scale = 1;
@ -200,29 +206,21 @@ bool LoadMerchandiseImpl(
// TODO: make page size and buy_activator positions configurable per "shelf" type (where is config stored?)
if (i % 9 == 0) {
ref_position = RE::NiPoint3(shelf_position.x + 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 110 + z_imbalance);
}
else if (i % 9 == 1) {
} else if (i % 9 == 1) {
ref_position = RE::NiPoint3(shelf_position.x + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 110 + z_imbalance);
}
else if (i % 9 == 2) {
} else if (i % 9 == 2) {
ref_position = RE::NiPoint3(shelf_position.x - 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 110 + z_imbalance);
}
else if (i % 9 == 3) {
} else if (i % 9 == 3) {
ref_position = RE::NiPoint3(shelf_position.x + 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 65 + z_imbalance);
}
else if (i % 9 == 4) {
} else if (i % 9 == 4) {
ref_position = RE::NiPoint3(shelf_position.x + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 65 + z_imbalance);
}
else if (i % 9 == 5) {
} else if (i % 9 == 5) {
ref_position = RE::NiPoint3(shelf_position.x - 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 65 + z_imbalance);
}
else if (i % 9 == 6) {
} else if (i % 9 == 6) {
ref_position = RE::NiPoint3(shelf_position.x + 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 20 + z_imbalance);
}
else if (i % 9 == 7) {
} else if (i % 9 == 7) {
ref_position = RE::NiPoint3(shelf_position.x + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 20 + z_imbalance);
}
else if (i % 9 == 8) {
} else if (i % 9 == 8) {
ref_position = RE::NiPoint3(shelf_position.x - 40 + x_imbalance, shelf_position.y + y_imbalance, shelf_position.z + 20 + z_imbalance);
}
MoveTo_Native(ref, ref->CreateRefHandle(), cell, cell->worldSpace, ref_position - RE::NiPoint3(10000, 10000, 10000), ref_angle);
@ -255,42 +253,42 @@ bool LoadMerchandiseImpl(
RE::TESObjectREFR * next_ref = merchant_shelf->GetLinkedRef(next_keyword);
if (!next_ref) {
logger::error("LoadMerchandise next_ref is null!");
regMap.SendEvent(false);
regMap.Unregister(toggle_ref);
return false;
failReg.SendEvent("Could not find the shelf's next button");
successReg.Unregister(toggle_ref);
failReg.Unregister(toggle_ref);
return;
}
RE::TESObjectREFR * prev_ref = merchant_shelf->GetLinkedRef(prev_keyword);
if (!prev_ref) {
logger::error("LoadMerchandise prev_ref is null!");
regMap.SendEvent(false);
regMap.Unregister(toggle_ref);
return false;
failReg.SendEvent("Could not find the shelf's previous button");
successReg.Unregister(toggle_ref);
failReg.Unregister(toggle_ref);
return;
}
toggle_ref->SetDisplayName("Clear merchandise", true);
if (page == max_page) {
next_ref->SetDisplayName("(No next page)", true);
}
else {
} else {
next_ref->SetDisplayName(fmt::format("Advance to page {:d}", page + 1).c_str(), true);
}
if (page == 1) {
prev_ref->SetDisplayName("(No previous page)", true);
}
else {
} else {
prev_ref->SetDisplayName(fmt::format("Back to page {:d}", page - 1).c_str(), true);
}
}
else {
} else {
const char * error = result.AsErr();
logger::error(FMT_STRING("LoadMerchandise get_merchandise_list error: {}"), error);
regMap.SendEvent(false);
regMap.Unregister(toggle_ref);
return false;
failReg.SendEvent(RE::BSFixedString(error));
successReg.Unregister(toggle_ref);
failReg.Unregister(toggle_ref);
return;
}
regMap.SendEvent(true);
regMap.Unregister(toggle_ref);
successReg.SendEvent(true);
successReg.Unregister(toggle_ref);
failReg.Unregister(toggle_ref);
});
return true;
}
bool ToggleMerchandise(
@ -305,8 +303,8 @@ bool ToggleMerchandise(
RE::BGSKeyword* item_keyword,
RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword)
{
RE::BGSKeyword* prev_keyword
) {
if (!merchant_shelf) {
logger::error("ToggleMerchandise merchant_shelf is null!");
return false;
@ -345,8 +343,7 @@ bool ToggleMerchandise(
next_ref->SetDisplayName("Load merchandise", true);
prev_ref->SetDisplayName("Load merchandise", true);
return true;
}
else {
} else {
// Load merchandise
int page = merchant_shelf->extraList.GetCount();
std::thread thread(LoadMerchandiseImpl, api_url, api_key, merchandise_list_id, merchant_shelf, placeholder_static, shelf_keyword, chest_keyword, item_keyword, toggle_keyword, next_keyword, prev_keyword, page);
@ -367,8 +364,8 @@ bool LoadNextMerchandise(
RE::BGSKeyword* item_keyword,
RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword)
{
RE::BGSKeyword* prev_keyword
) {
if (!merchant_shelf) {
logger::error("LoadNextMerchandise merchant_shelf is null!");
return false;
@ -398,8 +395,8 @@ bool LoadPrevMerchandise(
RE::BGSKeyword* item_keyword,
RE::BGSKeyword* toggle_keyword,
RE::BGSKeyword* next_keyword,
RE::BGSKeyword* prev_keyword)
{
RE::BGSKeyword* prev_keyword
) {
if (!merchant_shelf) {
logger::error("LoadPrevMerchandise merchant_shelf is null!");
return false;
@ -431,8 +428,7 @@ bool ReplaceMerch3D(RE::StaticFunctionTag*, RE::TESObjectREFR* merchant_shelf, R
RE::TESObjectCELL * cell = merchant_shelf->GetParentCell();
RE::FormID placeholder_form_id = placeholder_static->GetFormID();
RE::FormID shelf_form_id = merchant_shelf->GetFormID();
for (auto entry = cell->references.begin(); entry != cell->references.end(); ++entry)
{
for (auto entry = cell->references.begin(); entry != cell->references.end(); ++entry) {
RE::TESObjectREFR * ref = (*entry).get();
RE::TESBoundObject * base = ref->GetBaseObject();
if (base) {
@ -447,8 +443,7 @@ bool ReplaceMerch3D(RE::StaticFunctionTag*, RE::TESObjectREFR* merchant_shelf, R
if (linked_ref->Is3DLoaded()) {
logger::info("ReplaceMerch3D replaceing placeholder 3D with linked item 3D");
ref->Set3D(linked_ref->GetCurrent3D());
}
else {
} else {
logger::info("ReplaceMerch3D linked item ref 3D is not loaded yet, returning false");
return false;
}
@ -472,30 +467,28 @@ RE::TESForm * BuyMerchandise(RE::StaticFunctionTag*, RE::TESObjectREFR * merchan
return owner;
}
// Return code:
// -2: No changes to save, no create request was made
// -1: Error occured
// >= 0: ID of created MerchandiseList returned by API
int CreateMerchandiseListImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESObjectREFR* merchant_chest)
{
void CreateMerchandiseListImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESObjectREFR* merchant_chest) {
logger::info("Entered CreateMerchandiseListImpl");
RE::TESDataHandler * data_handler = RE::TESDataHandler::GetSingleton();
std::vector<MerchRecord> merch_records;
std::vector<RawMerchandise> merch_records;
if (!merchant_chest) {
logger::error("CreateMerchandiseList merchant_chest is null!");
return -1;
logger::error("CreateMerchandiseListImpl merchant_chest is null!");
return;
}
SKSE::RegistrationMap<int> regMap = SKSE::RegistrationMap<int>();
regMap.Register(merchant_chest, RE::BSFixedString("OnCreateMerchandise"));
SKSE::RegistrationMap<bool, int> successReg = SKSE::RegistrationMap<bool, int>();
successReg.Register(merchant_chest, RE::BSFixedString("OnCreateMerchandiseSuccess"));
SKSE::RegistrationMap<RE::BSFixedString> failReg = SKSE::RegistrationMap<RE::BSFixedString>();
failReg.Register(merchant_chest, RE::BSFixedString("OnCreateMerchandiseFail"));
RE::InventoryChanges * inventory_changes = merchant_chest->GetInventoryChanges();
if (inventory_changes == nullptr) {
logger::info("CreateMerchandiseList container empty, nothing to save");
regMap.SendEvent(-2);
regMap.Unregister(merchant_chest);
return -2;
successReg.SendEvent(false, -1);
successReg.Unregister(merchant_chest);
failReg.Unregister(merchant_chest);
return;
}
RE::BSSimpleList<RE::InventoryEntryData*>* entries = inventory_changes->entryList;
@ -505,6 +498,7 @@ int CreateMerchandiseListImpl(RE::BSFixedString api_url, RE::BSFixedString api_k
RE::InventoryEntryData * entry_data = *entry;
RE::TESBoundObject * base = entry_data->GetObject();
if (base) {
// Iterating through the entries extraList for debug logging info
RE::BSSimpleList<RE::ExtraDataList*> * x_lists = entry_data->extraLists;
if (x_lists) {
const char * entry_name = entry_data->extraLists->front()->GetDisplayName(base);
@ -562,11 +556,18 @@ int CreateMerchandiseListImpl(RE::BSFixedString api_url, RE::BSFixedString api_k
count++;
}
int merchandise_list_id = create_merchandise_list(api_url.c_str(), api_key.c_str(), shop_id, &merch_records[0], merch_records.size());
logger::info(FMT_STRING("CreateMerchandiseList create_merchandise_list result: {:d}"), merchandise_list_id);
regMap.SendEvent(merchandise_list_id);
regMap.Unregister(merchant_chest);
return merchandise_list_id;
FFIResult<int32_t> result = create_merchandise_list(api_url.c_str(), api_key.c_str(), shop_id, &merch_records[0], merch_records.size());
if (result.IsOk()) {
int32_t merchandise_list_id = result.AsOk();
logger::info(FMT_STRING("CreateMerchandiseList success: {}"), merchandise_list_id);
successReg.SendEvent(true, merchandise_list_id);
} else {
const char* error = result.AsErr();
logger::error(FMT_STRING("CreateMerchandiseList failure: {}"), error);
failReg.SendEvent(RE::BSFixedString(error));
}
successReg.Unregister(merchant_chest);
failReg.Unregister(merchant_chest);
}
bool CreateMerchandiseList(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t shop_id, RE::TESObjectREFR* merchant_chest) {

View File

@ -1,25 +1,30 @@
#include "bindings.h"
int CreateOwnerImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, RE::BSFixedString name, uint32_t mod_version, RE::TESQuest* quest)
{
void CreateOwnerImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, RE::BSFixedString name, uint32_t mod_version, RE::TESQuest* quest) {
logger::info("Entered CreateOwnerImpl");
if (!quest) {
logger::error("CreateOwner quest is null!");
return -1;
logger::error("CreateOwnerImpl quest is null!");
return;
}
SKSE::RegistrationMap<int> regMap = SKSE::RegistrationMap<int>();
regMap.Register(quest, RE::BSFixedString("OnCreateOwner"));
SKSE::RegistrationMap<int> successReg = SKSE::RegistrationMap<int>();
successReg.Register(quest, RE::BSFixedString("OnCreateOwnerSuccess"));
SKSE::RegistrationMap<RE::BSFixedString> failReg = SKSE::RegistrationMap<RE::BSFixedString>();
failReg.Register(quest, RE::BSFixedString("OnCreateOwnerFail"));
logger::info(FMT_STRING("CreateOwner api_url: {}"), api_url);
logger::info(FMT_STRING("CreateOwner api_key: {}"), api_key);
logger::info(FMT_STRING("CreateOwner name: {}"), name);
logger::info(FMT_STRING("CreateOwner mod_version: {}"), mod_version);
int owner_id = create_owner(api_url.c_str(), api_key.c_str(), name.c_str(), mod_version);
logger::info(FMT_STRING("CreateOwner result: {}"), owner_id);
regMap.SendEvent(owner_id);
regMap.Unregister(quest);
return owner_id;
logger::info(FMT_STRING("CreateOwner api_url: {}, api_key: {}, name: {}, mod_version: {}"), api_url, api_key, name, mod_version);
FFIResult<RawOwner> result = create_owner(api_url.c_str(), api_key.c_str(), name.c_str(), mod_version);
if (result.IsOk()) {
RawOwner owner = result.AsOk();
logger::info(FMT_STRING("CreateOwner success: {}"), owner.id);
successReg.SendEvent(owner.id);
} else {
RE::BSFixedString error = result.AsErr();
logger::info(FMT_STRING("CreateOwner failure: {}"), error);
failReg.SendEvent(error);
}
successReg.Unregister(quest);
failReg.Unregister(quest);
}
bool CreateOwner(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, RE::BSFixedString name, uint32_t mod_version, RE::TESQuest* quest) {
@ -34,30 +39,35 @@ bool CreateOwner(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedS
return true;
}
int UpdateOwnerImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t id, RE::BSFixedString name, uint32_t mod_version, RE::TESQuest* quest)
{
void UpdateOwnerImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t id, RE::BSFixedString name, uint32_t mod_version, RE::TESQuest* quest) {
logger::info("Entered UpdateOwnerImpl");
if (!quest) {
logger::error("UpdateOwner quest is null!");
return -1;
logger::error("UpdateOwnerImpl quest is null!");
return;
}
SKSE::RegistrationMap<int> regMap = SKSE::RegistrationMap<int>();
regMap.Register(quest, RE::BSFixedString("OnUpdateOwner"));
SKSE::RegistrationMap<int> successReg = SKSE::RegistrationMap<int>();
successReg.Register(quest, RE::BSFixedString("OnUpdateOwnerSuccess"));
SKSE::RegistrationMap<RE::BSFixedString> failReg = SKSE::RegistrationMap<RE::BSFixedString>();
failReg.Register(quest, RE::BSFixedString("OnUpdateOwnerFail"));
logger::info(FMT_STRING("UpdateOwner api_url: {}"), api_url);
logger::info(FMT_STRING("UpdateOwner api_key: {}"), api_key);
logger::info(FMT_STRING("UpdateOwner name: {}"), name);
logger::info(FMT_STRING("UpdateOwner mod_version: {}"), mod_version);
int owner_id = update_owner(api_url.c_str(), api_key.c_str(), id, name.c_str(), mod_version);
logger::info(FMT_STRING("UpdateOwner result: {}"), owner_id);
regMap.SendEvent(owner_id);
regMap.Unregister(quest);
return owner_id;
logger::info(FMT_STRING("UpdateOwner api_url: {}, api_key: {}, name: {}, mod_version: {}"), api_url, api_key, name, mod_version);
FFIResult<RawOwner> result = update_owner(api_url.c_str(), api_key.c_str(), id, name.c_str(), mod_version);
if (result.IsOk()) {
RawOwner owner = result.AsOk();
logger::info(FMT_STRING("UpdateOwner success: {}"), owner.id);
successReg.SendEvent(owner.id);
} else {
RE::BSFixedString error = result.AsErr();
logger::info(FMT_STRING("UpdateOwner failure: {}"), error);
failReg.SendEvent(error);
}
successReg.Unregister(quest);
failReg.Unregister(quest);
}
bool UpdateOwner(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t id, RE::BSFixedString name, uint32_t mod_version, RE::TESQuest* quest) {
logger::info("Entered CreateOwner");
logger::info("Entered UpdateOwner");
if (!quest) {
logger::error("UpdateOwner quest is null!");
return false;

View File

@ -1,10 +1,9 @@
#include "bindings.h"
void CreateShopImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, RE::BSFixedString name, RE::BSFixedString description, RE::TESQuest* quest)
{
void CreateShopImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, RE::BSFixedString name, RE::BSFixedString description, RE::TESQuest* quest) {
logger::info("Entered CreateShopImpl");
if (!quest) {
logger::error("CreateShop quest is null!");
logger::error("CreateShopImpl quest is null!");
return;
}
@ -13,19 +12,15 @@ void CreateShopImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, RE::BS
SKSE::RegistrationMap<RE::BSFixedString> failReg = SKSE::RegistrationMap<RE::BSFixedString>();
failReg.Register(quest, RE::BSFixedString("OnCreateShopFail"));
logger::info(FMT_STRING("CreateShop api_url: {}"), api_url);
logger::info(FMT_STRING("CreateShop api_key: {}"), api_key);
logger::info(FMT_STRING("CreateShop name: {}"), name);
logger::info(FMT_STRING("CreateShop description: {}"), description);
FFIResult<ShopRecord> result = create_shop(api_url.c_str(), api_key.c_str(), name.c_str(), description.c_str());
logger::info(FMT_STRING("CreateShop api_url: {}, api_key: {}, name: {}, description: {}"), api_url, api_key, name, description);
FFIResult<RawShop> result = create_shop(api_url.c_str(), api_key.c_str(), name.c_str(), description.c_str());
if (result.IsOk()) {
ShopRecord shop = result.AsOk();
logger::info(FMT_STRING("CreateShop result Ok: {:d}"), shop.id);
RawShop shop = result.AsOk();
logger::info(FMT_STRING("CreateShop success: {}"), shop.id);
successReg.SendEvent(shop.id, RE::BSFixedString(shop.name), RE::BSFixedString(shop.description));
}
else {
} else {
const char* error = result.AsErr();
logger::error(FMT_STRING("CreateShop result Err: {}"), error);
logger::error(FMT_STRING("CreateShop failure: {}"), error);
failReg.SendEvent(RE::BSFixedString(error));
}
successReg.Unregister(quest);
@ -44,11 +39,10 @@ bool CreateShop(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedSt
return true;
}
void UpdateShopImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t id, RE::BSFixedString name, RE::BSFixedString description, RE::TESQuest* quest)
{
void UpdateShopImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t id, RE::BSFixedString name, RE::BSFixedString description, RE::TESQuest* quest) {
logger::info("Entered UpdateShopImpl");
if (!quest) {
logger::error("UpdateShop quest is null!");
logger::error("UpdateShopImpl quest is null!");
return;
}
@ -57,19 +51,15 @@ void UpdateShopImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32
SKSE::RegistrationMap<RE::BSFixedString> failReg = SKSE::RegistrationMap<RE::BSFixedString>();
failReg.Register(quest, RE::BSFixedString("OnUpdateShopFail"));
logger::info(FMT_STRING("UpdateShop api_url: {}"), api_url);
logger::info(FMT_STRING("UpdateShop api_key: {}"), api_key);
logger::info(FMT_STRING("UpdateShop name: {}"), name);
logger::info(FMT_STRING("UpdateShop description: {}"), description);
FFIResult<ShopRecord> result = update_shop(api_url.c_str(), api_key.c_str(), id, name.c_str(), description.c_str());
logger::info(FMT_STRING("UpdateShop api_url: {}, api_key: {}, name: {}, description: {}"), api_url, api_key, name, description);
FFIResult<RawShop> result = update_shop(api_url.c_str(), api_key.c_str(), id, name.c_str(), description.c_str());
if (result.IsOk()) {
ShopRecord shop = result.AsOk();
logger::info(FMT_STRING("UpdateShop result Ok: {:d}"), shop.id);
RawShop shop = result.AsOk();
logger::info(FMT_STRING("UpdateShop success: {}"), shop.id);
successReg.SendEvent(shop.id, RE::BSFixedString(shop.name), RE::BSFixedString(shop.description));
}
else {
} else {
const char* error = result.AsErr();
logger::error(FMT_STRING("UpdateShop result Err: {}"), error);
logger::error(FMT_STRING("UpdateShop failure: {}"), error);
failReg.SendEvent(RE::BSFixedString(error));
}
successReg.Unregister(quest);
@ -88,11 +78,10 @@ bool UpdateShop(RE::StaticFunctionTag*, RE::BSFixedString api_url, RE::BSFixedSt
return true;
}
void GetShopImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t id, RE::TESQuest* quest)
{
void GetShopImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t id, RE::TESQuest* quest) {
logger::info("Entered GetShopImpl");
if (!quest) {
logger::error("GetShop quest is null!");
logger::error("GetShopImpl quest is null!");
return;
}
@ -103,15 +92,14 @@ void GetShopImpl(RE::BSFixedString api_url, RE::BSFixedString api_key, uint32_t
logger::info(FMT_STRING("GetShop api_url: {}"), api_url);
logger::info(FMT_STRING("GetShop api_key: {}"), api_key);
FFIResult<ShopRecord> result = get_shop(api_url.c_str(), api_key.c_str(), id);
FFIResult<RawShop> result = get_shop(api_url.c_str(), api_key.c_str(), id);
if (result.IsOk()) {
ShopRecord shop = result.AsOk();
logger::info(FMT_STRING("GetShop result Ok: {:d}"), shop.id);
RawShop shop = result.AsOk();
logger::info(FMT_STRING("GetShop success: {}"), shop.id);
successReg.SendEvent(shop.id, RE::BSFixedString(shop.name), RE::BSFixedString(shop.description));
}
else {
} else {
const char* error = result.AsErr();
logger::error(FMT_STRING("GetShop result Err: {}"), error);
logger::error(FMT_STRING("GetShop failure: {}"), error);
failReg.SendEvent(RE::BSFixedString(error));
}
successReg.Unregister(quest);