From 2bd43f6e7695fead88ae36610d8c1ef820b13652 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 18 Jul 2021 22:55:28 -0700 Subject: [PATCH 1/7] update changelog --- docs/changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.txt b/docs/changelog.txt index ded2ba8b4..cfb95eddf 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -43,6 +43,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: now displays which items are attached and which items are still missing for planned buildings +- `orders`: support reaction-specific item condition traits, like "lye-containing" for soap production orders - `tiletypes-here`, `tiletypes-here-point`: add --cursor and --quiet options to support non-interactive use cases ## Documentation From 37a8260c3bcc766baccd9fec59576c5aaef18b44 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sun, 18 Jul 2021 22:55:57 -0700 Subject: [PATCH 2/7] import and export reaction-specific item conditions --- plugins/orders.cpp | 90 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/plugins/orders.cpp b/plugins/orders.cpp index 629fee225..2a90e7e69 100644 --- a/plugins/orders.cpp +++ b/plugins/orders.cpp @@ -29,6 +29,8 @@ #include "df/manager_order.h" #include "df/manager_order_condition_item.h" #include "df/manager_order_condition_order.h" +#include "df/reaction.h" +#include "df/reaction_reagent.h" #include "df/world.h" using namespace DFHack; @@ -377,7 +379,26 @@ static command_result orders_export_command(color_ostream & out, const std::stri condition["tool"] = enum_item_key(it2->has_tool_use); } - // TODO: anon_1, anon_2, anon_3 + if (it2->min_dimension != -1) + { + condition["min_dimension"] = it2->min_dimension; + } + + if (it2->reaction_id != -1) + { + df::reaction *reaction = world->raws.reactions.reactions[it2->reaction_id]; + condition["reaction_id"] = reaction->code; + + if (!it2->contains.empty()) + { + Json::Value contains(Json::arrayValue); + for (int32_t contains_val : it2->contains) + { + contains.append(reaction->reagents[contains_val]->code); + } + condition["contains"] = contains; + } + } conditions.append(condition); } @@ -705,7 +726,72 @@ static command_result orders_import(color_ostream &out, Json::Value &orders) } } - // TODO: anon_1, anon_2, anon_3 + if (it2.isMember("min_dimension")) + { + condition->min_dimension = it2["min_dimension"].asInt(); + } + + if (it2.isMember("reaction_id")) + { + std::string reaction_code = it2["reaction_id"].asString(); + df::reaction *reaction = NULL; + int32_t reaction_id = -1; + size_t num_reactions = world->raws.reactions.reactions.size(); + for (size_t idx = 0; idx < num_reactions; ++idx) + { + reaction = world->raws.reactions.reactions[idx]; + if (reaction->code == reaction_code) + { + reaction_id = idx; + break; + } + } + if (reaction_id < 0) + { + delete condition; + + out << COLOR_YELLOW << "Reaction code not found for imported manager order: " << reaction_code << std::endl; + + continue; + } + + condition->reaction_id = reaction_id; + + if (it2.isMember("contains")) + { + size_t num_reagents = reaction->reagents.size(); + std::string bad_reagent_code; + for (Json::Value & contains_val : it2["contains"]) + { + std::string reagent_code = contains_val.asString(); + bool reagent_found = false; + for (size_t idx = 0; idx < num_reagents; ++idx) + { + df::reaction_reagent *reagent = reaction->reagents[idx]; + if (reagent->code == reagent_code) + { + condition->contains.push_back(idx); + reagent_found = true; + break; + } + } + + if (!reagent_found) + { + bad_reagent_code = reagent_code; + break; + } + } + if (!bad_reagent_code.empty()) + { + delete condition; + + out << COLOR_YELLOW << "Invalid reagent code for imported manager order: " << bad_reagent_code << std::endl; + + continue; + } + } + } order->item_conditions.push_back(condition); } From 98b707d44496ba7ff3b78e84f560527a4ec6d939 Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 19 Jul 2021 10:50:16 -0700 Subject: [PATCH 3/7] ensure active orders are restored after tests --- test/plugins/orders.lua | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/test/plugins/orders.lua b/test/plugins/orders.lua index 53b084f18..e07bdc032 100644 --- a/test/plugins/orders.lua +++ b/test/plugins/orders.lua @@ -1,7 +1,27 @@ config.mode = 'fortress' -TMP_FILE_NAME = 'tmp-test' -TMP_FILE_PATH = ('dfhack-config/orders/%s.json'):format(TMP_FILE_NAME) +local FILE_PATH_PATTERN = 'dfhack-config/orders/%s.json' + +local BACKUP_FILE_NAME = 'tmp-backup' +local BACKUP_FILE_PATH = FILE_PATH_PATTERN:format(BACKUP_FILE_NAME) + +local TMP_FILE_NAME = 'tmp-test' +local TMP_FILE_PATH = FILE_PATH_PATTERN:format(TMP_FILE_NAME) + +local function test_wrapper(test_fn) + -- save active orders + dfhack.run_command_silent{'orders', 'export', BACKUP_FILE_NAME} + return dfhack.with_finalize( + function() + -- clear test orders, restore original orders, remove temp file + dfhack.run_command_silent{'orders', 'clear'} + print('reimporting') + dfhack.run_command_silent{'orders', 'import', BACKUP_FILE_NAME} + os.remove(BACKUP_FILE_PATH) + end, + test_fn) +end +config.wrapper = test_wrapper function run_orders_import(file_content) local f = io.open(TMP_FILE_PATH, 'w') From 9ec1488f1b730a3010c117bb9015c98385b2134a Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 19 Jul 2021 11:43:09 -0700 Subject: [PATCH 4/7] add unit tests --- test/plugins/orders.lua | 142 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 129 insertions(+), 13 deletions(-) diff --git a/test/plugins/orders.lua b/test/plugins/orders.lua index e07bdc032..0982de807 100644 --- a/test/plugins/orders.lua +++ b/test/plugins/orders.lua @@ -9,38 +9,61 @@ local TMP_FILE_NAME = 'tmp-test' local TMP_FILE_PATH = FILE_PATH_PATTERN:format(TMP_FILE_NAME) local function test_wrapper(test_fn) - -- save active orders + -- backup and clear active orders dfhack.run_command_silent{'orders', 'export', BACKUP_FILE_NAME} + dfhack.run_command_silent{'orders', 'clear'} + df.global.world.manager_order_next_id = 0 return dfhack.with_finalize( function() - -- clear test orders, restore original orders, remove temp file + -- clear test orders, restore original orders, remove temp files dfhack.run_command_silent{'orders', 'clear'} - print('reimporting') dfhack.run_command_silent{'orders', 'import', BACKUP_FILE_NAME} + df.global.world.manager_order_next_id = + #df.global.world.manager_orders os.remove(BACKUP_FILE_PATH) + os.remove(TMP_FILE_PATH) end, test_fn) end config.wrapper = test_wrapper +-- returns export command result and exported file content +function run_orders_export() + local _, result = dfhack.run_command_silent{'orders', 'export', + TMP_FILE_NAME} + local f = io.open(TMP_FILE_PATH, 'r') + return dfhack.with_finalize( + function() f:close() end, + function() return result, f:read('*all') end) +end + function run_orders_import(file_content) local f = io.open(TMP_FILE_PATH, 'w') f:write(file_content) f:close() + return dfhack.run_command_silent{'orders', 'import', TMP_FILE_NAME} +end - return dfhack.with_finalize( - function() - os.remove(TMP_FILE_PATH) - end, - function() - return dfhack.run_command_silent{'orders', 'import', TMP_FILE_NAME} - end - ) +local function normalize_whitespace(str) + return str:gsub('%s+', ' '):trim() +end + +function check_export_success(expected_file_content) + local result, file_content = run_orders_export() + expect.eq(result, CR_OK) + + -- ignore whitespace (otherwise the expected file content is impossible to + -- format properly in this file) + expect.eq(normalize_whitespace(expected_file_content), + normalize_whitespace(file_content)) end -function check_import_success(file_content) +function check_import_success(file_content, comment, num_expected_orders) + local prev_num_orders = #df.global.world.manager_orders local output, result = run_orders_import(file_content) expect.eq(result, CR_OK) + expect.eq(prev_num_orders + num_expected_orders, + #df.global.world.manager_orders) end function check_import_fail(file_content, comment, prefix) @@ -55,7 +78,7 @@ function check_import_fail(file_content, comment, prefix) end function test.import_empty() - check_import_success('[]') + check_import_success('[]', 'empty input', 0) end function test.import_non_array() @@ -141,3 +164,96 @@ function test.import_valid_and_invalid_orders() ] ]], 'empty order before valid order') end + +function test.import_export_reaction_condition() + local file_content = [[ + [ + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "Daily", + "id" : 0, + "is_active" : false, + "is_validated" : false, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "contains" : + [ + "lye" + ], + "reaction_id" : "MAKE_SOAP_FROM_TALLOW", + "value" : 5 + } + ], + "job" : "CustomReaction", + "reaction" : "MAKE_SOAP_FROM_TALLOW" + } + ] + ]] + check_import_success(file_content, 'valid reaction condition', 1) + check_export_success(file_content) +end + +local function get_last_order() + return df.global.world.manager_orders[#df.global.world.manager_orders-1] +end + +function test.import_invalid_reaction_conditions() + check_import_success([[ + [ + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "OneTime", + "id" : 0, + "is_active" : false, + "is_validated" : true, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "contains" : + [ + "lye" + ], + "reaction_id" : "MAKE_SOAP_FROM_TALLOW_xxx", + "value" : 5 + } + ], + "job" : "CustomReaction", + "reaction" : "MAKE_SOAP_FROM_TALLOW" + } + ] + ]], 'condition ignored for bad reaction id', 1) + expect.eq(0, #get_last_order().item_conditions) + + check_import_success([[ + [ + { + "amount_left" : 1, + "amount_total" : 1, + "frequency" : "OneTime", + "id" : 0, + "is_active" : false, + "is_validated" : true, + "item_conditions" : + [ + { + "condition" : "AtLeast", + "contains" : + [ + "lye_xxx" + ], + "reaction_id" : "MAKE_SOAP_FROM_TALLOW", + "value" : 5 + } + ], + "job" : "CustomReaction", + "reaction" : "MAKE_SOAP_FROM_TALLOW" + } + ] + ]], 'condition ignored for bad reagent name', 1) + expect.eq(0, #get_last_order().item_conditions) +end From 9a7a692ea82d50fc70c1ea1f4c3ef520e548cbff Mon Sep 17 00:00:00 2001 From: myk002 Date: Mon, 19 Jul 2021 13:54:22 -0700 Subject: [PATCH 5/7] ensure restored orders start at index 0 so the manager_order_next_id state can be set to #orders --- test/plugins/orders.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/test/plugins/orders.lua b/test/plugins/orders.lua index 0982de807..bb84b9166 100644 --- a/test/plugins/orders.lua +++ b/test/plugins/orders.lua @@ -17,6 +17,7 @@ local function test_wrapper(test_fn) function() -- clear test orders, restore original orders, remove temp files dfhack.run_command_silent{'orders', 'clear'} + df.global.world.manager_order_next_id = 0 dfhack.run_command_silent{'orders', 'import', BACKUP_FILE_NAME} df.global.world.manager_order_next_id = #df.global.world.manager_orders From 00adb1b7205629e386e6c061d407b103cd752d5e Mon Sep 17 00:00:00 2001 From: myk002 Date: Tue, 20 Jul 2021 18:06:41 -0700 Subject: [PATCH 6/7] update changelog --- docs/changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index cfb95eddf..742782a4f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -43,7 +43,7 @@ changelog.txt uses a syntax similar to RST, with a few special sequences: ## Misc Improvements - `buildingplan`: now displays which items are attached and which items are still missing for planned buildings -- `orders`: support reaction-specific item condition traits, like "lye-containing" for soap production orders +- `orders`: support importing and exporting reaction-specific item conditions, like "lye-containing" for soap production orders - `tiletypes-here`, `tiletypes-here-point`: add --cursor and --quiet options to support non-interactive use cases ## Documentation From f47bab9806d0a82ac67dace7f429e74ac24988a1 Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 14 Aug 2021 16:43:40 -0700 Subject: [PATCH 7/7] pass comment param to expect calls --- test/plugins/orders.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/plugins/orders.lua b/test/plugins/orders.lua index bb84b9166..95fe186d2 100644 --- a/test/plugins/orders.lua +++ b/test/plugins/orders.lua @@ -62,9 +62,9 @@ end function check_import_success(file_content, comment, num_expected_orders) local prev_num_orders = #df.global.world.manager_orders local output, result = run_orders_import(file_content) - expect.eq(result, CR_OK) + expect.eq(result, CR_OK, comment) expect.eq(prev_num_orders + num_expected_orders, - #df.global.world.manager_orders) + #df.global.world.manager_orders, comment) end function check_import_fail(file_content, comment, prefix)