Merge remote-tracking branches 'RossM/autolabor-changes', 'RossM/workflow-changes' and 'lethosor/plugin-exports' into develop

develop
lethosor 2015-05-09 09:39:55 -04:00
commit a8f5e683f7
5 changed files with 139 additions and 41 deletions

@ -277,6 +277,7 @@ bool Plugin::load(color_ostream &con)
plugin_enable = (command_result (*)(color_ostream &,bool)) LookupPlugin(plug, "plugin_enable"); plugin_enable = (command_result (*)(color_ostream &,bool)) LookupPlugin(plug, "plugin_enable");
plugin_is_enabled = (bool*) LookupPlugin(plug, "plugin_is_enabled"); plugin_is_enabled = (bool*) LookupPlugin(plug, "plugin_is_enabled");
plugin_eval_ruby = (command_result (*)(color_ostream &, const char*)) LookupPlugin(plug, "plugin_eval_ruby"); plugin_eval_ruby = (command_result (*)(color_ostream &, const char*)) LookupPlugin(plug, "plugin_eval_ruby");
plugin_get_exports = (PluginExports* (*)(void)) LookupPlugin(plug, "plugin_get_exports");
index_lua(plug); index_lua(plug);
this->name = *plug_name; this->name = *plug_name;
plugin_lib = plug; plugin_lib = plug;
@ -531,6 +532,16 @@ Plugin::plugin_state Plugin::getState() const
return state; return state;
} }
PluginExports *Plugin::getExports()
{
if (!plugin_get_exports)
return NULL;
PluginExports *exports = plugin_get_exports();
if (!exports->bind(plugin_lib))
return NULL;
return exports;
};
void Plugin::index_lua(DFLibrary *lib) void Plugin::index_lua(DFLibrary *lib)
{ {
if (auto cmdlist = (CommandReg*)LookupPlugin(lib, "plugin_lua_commands")) if (auto cmdlist = (CommandReg*)LookupPlugin(lib, "plugin_lua_commands"))
@ -703,6 +714,19 @@ void Plugin::push_function(lua_State *state, LuaFunction *fn)
lua_pushcclosure(state, lua_fun_wrapper, 4); lua_pushcclosure(state, lua_fun_wrapper, 4);
} }
bool PluginExports::bind(DFLibrary *lib)
{
for (auto it = bindings.begin(); it != bindings.end(); ++it)
{
std::string name = it->first;
void** dest = it->second;
*dest = LookupPlugin(lib, name.c_str());
if (!*dest)
return false;
}
return true;
}
PluginManager::PluginManager(Core * core) PluginManager::PluginManager(Core * core)
{ {
cmdlist_mutex = new mutex(); cmdlist_mutex = new mutex();
@ -766,6 +790,16 @@ Plugin *PluginManager::getPluginByCommand(const std::string &command)
return NULL; return NULL;
} }
void *PluginManager::getPluginExports(const std::string &name)
{
Plugin *plug = getPluginByName(name);
if (!plug)
return NULL;
if (plug->getState() != Plugin::plugin_state::PS_LOADED)
return NULL;
return plug->getExports();
}
// FIXME: handle name collisions... // FIXME: handle name collisions...
command_result PluginManager::InvokeCommand(color_ostream &out, const std::string & command, std::vector <std::string> & parameters) command_result PluginManager::InvokeCommand(color_ostream &out, const std::string & command, std::vector <std::string> & parameters)
{ {

@ -50,6 +50,7 @@ namespace df
namespace DFHack namespace DFHack
{ {
class Core; class Core;
class PluginExports;
class PluginManager; class PluginManager;
class virtual_identity; class virtual_identity;
class RPCService; class RPCService;
@ -165,6 +166,7 @@ namespace DFHack
command_result invoke(color_ostream &out, const std::string & command, std::vector <std::string> & parameters); command_result invoke(color_ostream &out, const std::string & command, std::vector <std::string> & parameters);
bool can_invoke_hotkey(const std::string & command, df::viewscreen *top ); bool can_invoke_hotkey(const std::string & command, df::viewscreen *top );
plugin_state getState () const; plugin_state getState () const;
PluginExports *getExports();
RPCService *rpc_connect(color_ostream &out); RPCService *rpc_connect(color_ostream &out);
@ -227,7 +229,15 @@ namespace DFHack
command_result (*plugin_enable)(color_ostream &, bool); command_result (*plugin_enable)(color_ostream &, bool);
RPCService* (*plugin_rpcconnect)(color_ostream &); RPCService* (*plugin_rpcconnect)(color_ostream &);
command_result (*plugin_eval_ruby)(color_ostream &, const char*); command_result (*plugin_eval_ruby)(color_ostream &, const char*);
PluginExports* (*plugin_get_exports)(void);
}; };
class DFHACK_EXPORT PluginExports {
protected:
friend class Plugin;
std::map<std::string, void**> bindings;
bool bind(DFLibrary* lib);
};
#define PLUGIN_EXPORT_BIND(name) bindings.insert(std::pair<std::string, void**>(#name, (void**)&this->name))
class DFHACK_EXPORT PluginManager class DFHACK_EXPORT PluginManager
{ {
// PRIVATE METHODS // PRIVATE METHODS
@ -244,6 +254,7 @@ namespace DFHack
public: public:
Plugin *getPluginByName (const std::string & name); Plugin *getPluginByName (const std::string & name);
Plugin *getPluginByCommand (const std::string &command); Plugin *getPluginByCommand (const std::string &command);
void *getPluginExports(const std::string &name);
command_result InvokeCommand(color_ostream &out, const std::string & command, std::vector <std::string> & parameters); command_result InvokeCommand(color_ostream &out, const std::string & command, std::vector <std::string> & parameters);
bool CanInvokeHotkey(const std::string &command, df::viewscreen *top); bool CanInvokeHotkey(const std::string &command, df::viewscreen *top);
Plugin* operator[] (std::size_t index) Plugin* operator[] (std::size_t index)
@ -293,6 +304,17 @@ namespace DFHack
DFhackDataExport bool plugin_is_enabled = false; \ DFhackDataExport bool plugin_is_enabled = false; \
bool &varname = plugin_is_enabled; bool &varname = plugin_is_enabled;
#define DFHACK_PLUGIN_EXPORTS(clsname) \
DFhackCExport PluginExports* plugin_get_exports() \
{ \
static clsname* instance = NULL; \
if (!instance) \
instance = new clsname; \
return (PluginExports*)instance; \
}
#define GET_PLUGIN_EXPORTS(plugname, clsname) \
(clsname*)DFHack::Core::getInstance().getPluginManager()->getPluginExports(plugname)
#define DFHACK_PLUGIN_LUA_COMMANDS \ #define DFHACK_PLUGIN_LUA_COMMANDS \
DFhackCExport const DFHack::CommandReg plugin_lua_commands[] = DFhackCExport const DFHack::CommandReg plugin_lua_commands[] =
#define DFHACK_PLUGIN_LUA_FUNCTIONS \ #define DFHACK_PLUGIN_LUA_FUNCTIONS \

@ -396,6 +396,11 @@ bool MaterialInfo::matches(const df::job_material_category &cat)
using namespace df::enums::material_flags; using namespace df::enums::material_flags;
TEST(plant, STRUCTURAL_PLANT_MAT); TEST(plant, STRUCTURAL_PLANT_MAT);
TEST(plant, SEED_MAT);
TEST(plant, THREAD_PLANT);
TEST(plant, ALCOHOL_PLANT);
TEST(plant, POWDER_MISC_PLANT);
TEST(plant, LIQUID_MISC_PLANT);
TEST(wood, WOOD); TEST(wood, WOOD);
TEST(cloth, THREAD_PLANT); TEST(cloth, THREAD_PLANT);
TEST(silk, SILK); TEST(silk, SILK);

@ -109,6 +109,9 @@ enum dwarf_state {
// Busy with a useful task // Busy with a useful task
BUSY, BUSY,
// Busy with a useful task that requires a tool
EXCLUSIVE,
// In the military, can't work // In the military, can't work
MILITARY, MILITARY,
@ -119,11 +122,12 @@ enum dwarf_state {
OTHER OTHER
}; };
const int NUM_STATE = 5; const int NUM_STATE = 6;
static const char *state_names[] = { static const char *state_names[] = {
"IDLE", "IDLE",
"BUSY", "BUSY",
"EXCLUSIVE",
"MILITARY", "MILITARY",
"CHILD", "CHILD",
"OTHER", "OTHER",
@ -133,13 +137,13 @@ static const dwarf_state dwarf_states[] = {
BUSY /* CarveFortification */, BUSY /* CarveFortification */,
BUSY /* DetailWall */, BUSY /* DetailWall */,
BUSY /* DetailFloor */, BUSY /* DetailFloor */,
BUSY /* Dig */, EXCLUSIVE /* Dig */,
BUSY /* CarveUpwardStaircase */, EXCLUSIVE /* CarveUpwardStaircase */,
BUSY /* CarveDownwardStaircase */, EXCLUSIVE /* CarveDownwardStaircase */,
BUSY /* CarveUpDownStaircase */, EXCLUSIVE /* CarveUpDownStaircase */,
BUSY /* CarveRamp */, EXCLUSIVE /* CarveRamp */,
BUSY /* DigChannel */, EXCLUSIVE /* DigChannel */,
BUSY /* FellTree */, EXCLUSIVE /* FellTree */,
BUSY /* GatherPlants */, BUSY /* GatherPlants */,
BUSY /* RemoveConstruction */, BUSY /* RemoveConstruction */,
BUSY /* CollectWebs */, BUSY /* CollectWebs */,
@ -154,7 +158,7 @@ static const dwarf_state dwarf_states[] = {
OTHER /* Sleep */, OTHER /* Sleep */,
BUSY /* CollectSand */, BUSY /* CollectSand */,
BUSY /* Fish */, BUSY /* Fish */,
BUSY /* Hunt */, EXCLUSIVE /* Hunt */,
OTHER /* HuntVermin */, OTHER /* HuntVermin */,
BUSY /* Kidnap */, BUSY /* Kidnap */,
BUSY /* BeatCriminal */, BUSY /* BeatCriminal */,
@ -183,7 +187,7 @@ static const dwarf_state dwarf_states[] = {
OTHER /* GoShopping2 */, OTHER /* GoShopping2 */,
BUSY /* Clean */, BUSY /* Clean */,
OTHER /* Rest */, OTHER /* Rest */,
BUSY /* PickupEquipment */, EXCLUSIVE /* PickupEquipment */,
BUSY /* DumpItem */, BUSY /* DumpItem */,
OTHER /* StrangeMoodCrafter */, OTHER /* StrangeMoodCrafter */,
OTHER /* StrangeMoodJeweller */, OTHER /* StrangeMoodJeweller */,
@ -393,8 +397,15 @@ struct labor_default
int active_dwarfs; int active_dwarfs;
}; };
// The percentage of the dwarves assigned as haulers at any one time.
static int hauler_pct = 33; static int hauler_pct = 33;
// The maximum percentage of dwarves who will be allowed to be idle.
// Decreasing this will encourage autolabor to keep dwarves busy,
// at the expense of making it harder for dwarves to specialize in
// specific skills.
static int idler_pct = 10;
static std::vector<struct labor_info> labor_infos; static std::vector<struct labor_info> labor_infos;
static const struct labor_default default_labor_infos[] = { static const struct labor_default default_labor_infos[] = {
@ -521,7 +532,6 @@ struct dwarf_info_t
bool medical; // this dwarf has medical responsibility bool medical; // this dwarf has medical responsibility
bool trader; // this dwarf has trade responsibility bool trader; // this dwarf has trade responsibility
bool diplomacy; // this dwarf meets with diplomats bool diplomacy; // this dwarf meets with diplomats
int single_labor; // this dwarf will be exclusively assigned to one labor (-1/NONE for none)
}; };
static bool isOptionEnabled(unsigned flag) static bool isOptionEnabled(unsigned flag)
@ -738,7 +748,13 @@ struct laborinfo_sorter
{ {
bool operator() (int i,int j) bool operator() (int i,int j)
{ {
if (labor_infos[i].mode() != labor_infos[j].mode())
return labor_infos[i].mode() < labor_infos[j].mode(); return labor_infos[i].mode() < labor_infos[j].mode();
if (labor_infos[i].is_exclusive != labor_infos[j].is_exclusive)
return labor_infos[i].is_exclusive;
if (labor_infos[i].maximum_dwarfs() != labor_infos[j].maximum_dwarfs())
return labor_infos[i].maximum_dwarfs() < labor_infos[j].maximum_dwarfs();
return false;
}; };
}; };
@ -769,6 +785,7 @@ static void assign_labor(unit_labor::unit_labor labor,
int best_dwarf = 0; int best_dwarf = 0;
int best_value = -10000; int best_value = -10000;
int best_skill = 0;
std::vector<int> values(n_dwarfs); std::vector<int> values(n_dwarfs);
std::vector<int> candidates; std::vector<int> candidates;
@ -813,6 +830,9 @@ static void assign_labor(unit_labor::unit_labor labor,
dwarf_skill[dwarf] = skill_level; dwarf_skill[dwarf] = skill_level;
dwarf_skillxp[dwarf] = skill_experience; dwarf_skillxp[dwarf] = skill_experience;
if (best_skill < skill_level)
best_skill = skill_level;
value += skill_level * 100; value += skill_level * 100;
value += skill_experience / 20; value += skill_experience / 20;
if (skill_level > 0 || skill_experience > 0) if (skill_level > 0 || skill_experience > 0)
@ -832,6 +852,9 @@ static void assign_labor(unit_labor::unit_labor labor,
value += 350; value += 350;
} }
if (dwarf_info[dwarf].has_exclusive_labor)
value -= 500;
// bias by happiness // bias by happiness
//value += dwarfs[dwarf]->status.happiness; //value += dwarfs[dwarf]->status.happiness;
@ -897,15 +920,28 @@ static void assign_labor(unit_labor::unit_labor labor,
if (unit_labor::FISH == labor && !has_fishery) if (unit_labor::FISH == labor && !has_fishery)
min_dwarfs = max_dwarfs = 0; min_dwarfs = max_dwarfs = 0;
bool want_idle_dwarf = true; // If there are enough idle dwarves to choose from, enter an aggressive assignment
if (state_count[IDLE] < 2) // mode. "Enough" idle dwarves is defined as 2 or 10% of the total number of dwarves,
want_idle_dwarf = false; // whichever is higher.
//
// In aggressive mode, we will always pick at least one idle dwarf for each skill,
// in order to try to get the idle dwarves to start doing something. We also pick
// any dwarf more preferable to the idle dwarf, since we'd rather have a more
// preferable dwarf do a new job if one becomes available (probably because that
// dwarf just finished a job).
//
// In non-aggressive mode, only dwarves that are good at a labor will be assigned
// to it. Dwarves good at nothing, or nothing that needs doing, will tend to get
// assigned to hauling by the hauler code. If there are no hauling jobs to do,
// they will sit around idle and when enough build up they will trigger aggressive
// mode again.
bool aggressive_mode = state_count[IDLE] >= 2 && state_count[IDLE] >= n_dwarfs * idler_pct / 100;
/* /*
* Assign dwarfs to this labor. We assign at least the minimum number of dwarfs, in * Assign dwarfs to this labor. We assign at least the minimum number of dwarfs, in
* order of preference, and then assign additional dwarfs that meet any of these conditions: * order of preference, and then assign additional dwarfs that meet any of these conditions:
* - The dwarf is idle and there are no idle dwarves assigned to this labor * - We are in aggressive mode and have not yet assigned an idle dwarf
* - The dwarf has nonzero skill associated with the labor * - The dwarf is good at this skill
* - The labor is mining, hunting, or woodcutting and the dwarf currently has it enabled. * - The labor is mining, hunting, or woodcutting and the dwarf currently has it enabled.
* We stop assigning dwarfs when we reach the maximum allowed. * We stop assigning dwarfs when we reach the maximum allowed.
* Note that only idle and busy dwarfs count towards the number of dwarfs. "Other" dwarfs * Note that only idle and busy dwarfs count towards the number of dwarfs. "Other" dwarfs
@ -917,24 +953,23 @@ static void assign_labor(unit_labor::unit_labor labor,
{ {
int dwarf = candidates[i]; int dwarf = candidates[i];
if (dwarf_info[dwarf].trader && trader_requested)
continue;
if (dwarf_info[dwarf].diplomacy)
continue;
assert(dwarf >= 0); assert(dwarf >= 0);
assert(dwarf < n_dwarfs); assert(dwarf < n_dwarfs);
bool preferred_dwarf = false; bool preferred_dwarf = false;
if (want_idle_dwarf && dwarf_info[dwarf].state == IDLE) if (dwarf_skillxp[dwarf] > 0 && dwarf_skill[dwarf] >= best_skill / 2)
preferred_dwarf = true;
if (dwarf_skill[dwarf] > 0)
preferred_dwarf = true; preferred_dwarf = true;
if (previously_enabled[dwarf] && labor_infos[labor].is_exclusive) if (previously_enabled[dwarf] && labor_infos[labor].is_exclusive && dwarf_info[dwarf].state == EXCLUSIVE)
preferred_dwarf = true; preferred_dwarf = true;
if (dwarf_info[dwarf].medical && labor == df::unit_labor::DIAGNOSE) if (dwarf_info[dwarf].medical && labor == df::unit_labor::DIAGNOSE)
preferred_dwarf = true; preferred_dwarf = true;
if (dwarf_info[dwarf].trader && trader_requested)
continue;
if (dwarf_info[dwarf].diplomacy)
continue;
if (labor_infos[labor].active_dwarfs >= min_dwarfs && !preferred_dwarf) if (labor_infos[labor].active_dwarfs >= min_dwarfs && !preferred_dwarf && !aggressive_mode)
continue; continue;
if (!dwarfs[dwarf]->status.labors[labor]) if (!dwarfs[dwarf]->status.labors[labor])
@ -952,11 +987,11 @@ static void assign_labor(unit_labor::unit_labor labor,
if (print_debug) if (print_debug)
out.print("Dwarf %i \"%s\" assigned %s: value %i %s %s\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), ENUM_KEY_STR(unit_labor, labor).c_str(), values[dwarf], dwarf_info[dwarf].trader ? "(trader)" : "", dwarf_info[dwarf].diplomacy ? "(diplomacy)" : ""); out.print("Dwarf %i \"%s\" assigned %s: value %i %s %s\n", dwarf, dwarfs[dwarf]->name.first_name.c_str(), ENUM_KEY_STR(unit_labor, labor).c_str(), values[dwarf], dwarf_info[dwarf].trader ? "(trader)" : "", dwarf_info[dwarf].diplomacy ? "(diplomacy)" : "");
if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY || dwarf_info[dwarf].state == EXCLUSIVE)
labor_infos[labor].active_dwarfs++; labor_infos[labor].active_dwarfs++;
if (dwarf_info[dwarf].state == IDLE) if (dwarf_info[dwarf].state == IDLE)
want_idle_dwarf = false; aggressive_mode = false;
} }
} }
@ -1049,8 +1084,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
{ {
dwarf_info[dwarf].single_labor = -1;
if (dwarfs[dwarf]->status.souls.size() <= 0) if (dwarfs[dwarf]->status.souls.size() <= 0)
continue; continue;
@ -1214,7 +1247,9 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
laborinfo_sorter lasorter; laborinfo_sorter lasorter;
std::sort(labors.begin(), labors.end(), lasorter); std::sort(labors.begin(), labors.end(), lasorter);
// Handle DISABLED skills (just bookkeeping) // Handle DISABLED skills (just bookkeeping).
// Note that autolabor should *NEVER* enable or disable a skill that has been marked as DISABLED, for any reason.
// The user has told us that they want manage this skill manually, and we must respect that.
for (auto lp = labors.begin(); lp != labors.end(); ++lp) for (auto lp = labors.begin(); lp != labors.end(); ++lp)
{ {
auto labor = *lp; auto labor = *lp;
@ -1224,12 +1259,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
for (int dwarf = 0; dwarf < n_dwarfs; dwarf++) for (int dwarf = 0; dwarf < n_dwarfs; dwarf++)
{ {
if ((dwarf_info[dwarf].trader && trader_requested) ||
dwarf_info[dwarf].diplomacy)
{
dwarfs[dwarf]->status.labors[labor] = false;
}
if (dwarfs[dwarf]->status.labors[labor]) if (dwarfs[dwarf]->status.labors[labor])
{ {
if (labor_infos[labor].is_exclusive) if (labor_infos[labor].is_exclusive)
@ -1252,7 +1281,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
// Set about 1/3 of the dwarfs as haulers. The haulers have all HAULER labors enabled. Having a lot of haulers helps // Set about 1/3 of the dwarfs as haulers. The haulers have all HAULER labors enabled. Having a lot of haulers helps
// make sure that hauling jobs are handled quickly rather than building up. // make sure that hauling jobs are handled quickly rather than building up.
int num_haulers = state_count[IDLE] + state_count[BUSY] * hauler_pct / 100; int num_haulers = state_count[IDLE] + (state_count[BUSY] + state_count[EXCLUSIVE]) * hauler_pct / 100;
if (num_haulers < 1) if (num_haulers < 1)
num_haulers = 1; num_haulers = 1;
@ -1274,7 +1303,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
continue; continue;
} }
if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY || dwarf_info[dwarf].state == EXCLUSIVE)
hauler_ids.push_back(dwarf); hauler_ids.push_back(dwarf);
} }
dwarfinfo_sorter sorter(dwarf_info); dwarfinfo_sorter sorter(dwarf_info);
@ -1305,7 +1334,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
dwarfs[dwarf]->status.labors[labor] = true; dwarfs[dwarf]->status.labors[labor] = true;
dwarf_info[dwarf].assigned_jobs++; dwarf_info[dwarf].assigned_jobs++;
if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY) if (dwarf_info[dwarf].state == IDLE || dwarf_info[dwarf].state == BUSY || dwarf_info[dwarf].state == EXCLUSIVE)
labor_infos[labor].active_dwarfs++; labor_infos[labor].active_dwarfs++;
if (print_debug) if (print_debug)

@ -203,12 +203,20 @@ for job,flag in pairs(plant_products) do
local itag = 'idx_'..string.lower(flag) local itag = 'idx_'..string.lower(flag)
job_outputs[job] = function(callback, job) job_outputs[job] = function(callback, job)
local mat_type, mat_index = -1, -1 local mat_type, mat_index = -1, -1
local seed_type, seed_index = -1, -1
local mat = dfhack.matinfo.decode(job.job_items[0]) local mat = dfhack.matinfo.decode(job.job_items[0])
if mat and mat.plant and mat.plant.flags[flag] then if mat and mat.plant and mat.plant.flags[flag] then
mat_type = mat.plant.material_defs[ttag] mat_type = mat.plant.material_defs[ttag]
mat_index = mat.plant.material_defs[itag] mat_index = mat.plant.material_defs[itag]
seed_type = mat.plant.material_defs['type_seed']
seed_index = mat.plant.material_defs['idx_seed']
end end
default_output(callback, job, mat_type, mat_index) local mat_mask = { }
if flag ~= 'LEAVES' then
mat_mask.plant = true
end
default_output(callback, job, mat_type, mat_index, mat_mask)
callback{ item_type = df.item_type.SEEDS, mat_type = seed_type, mat_index = seed_index }
end end
end end