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_is_enabled = (bool*) LookupPlugin(plug, "plugin_is_enabled");
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);
this->name = *plug_name;
plugin_lib = plug;
@ -531,6 +532,16 @@ Plugin::plugin_state Plugin::getState() const
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)
{
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);
}
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)
{
cmdlist_mutex = new mutex();
@ -766,6 +790,16 @@ Plugin *PluginManager::getPluginByCommand(const std::string &command)
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...
command_result PluginManager::InvokeCommand(color_ostream &out, const std::string & command, std::vector <std::string> & parameters)
{

@ -50,6 +50,7 @@ namespace df
namespace DFHack
{
class Core;
class PluginExports;
class PluginManager;
class virtual_identity;
class RPCService;
@ -165,6 +166,7 @@ namespace DFHack
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 );
plugin_state getState () const;
PluginExports *getExports();
RPCService *rpc_connect(color_ostream &out);
@ -227,7 +229,15 @@ namespace DFHack
command_result (*plugin_enable)(color_ostream &, bool);
RPCService* (*plugin_rpcconnect)(color_ostream &);
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
{
// PRIVATE METHODS
@ -244,6 +254,7 @@ namespace DFHack
public:
Plugin *getPluginByName (const std::string & name);
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);
bool CanInvokeHotkey(const std::string &command, df::viewscreen *top);
Plugin* operator[] (std::size_t index)
@ -293,6 +304,17 @@ namespace DFHack
DFhackDataExport bool plugin_is_enabled = false; \
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 \
DFhackCExport const DFHack::CommandReg plugin_lua_commands[] =
#define DFHACK_PLUGIN_LUA_FUNCTIONS \

@ -396,6 +396,11 @@ bool MaterialInfo::matches(const df::job_material_category &cat)
using namespace df::enums::material_flags;
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(cloth, THREAD_PLANT);
TEST(silk, SILK);

@ -109,6 +109,9 @@ enum dwarf_state {
// Busy with a useful task
BUSY,
// Busy with a useful task that requires a tool
EXCLUSIVE,
// In the military, can't work
MILITARY,
@ -119,11 +122,12 @@ enum dwarf_state {
OTHER
};
const int NUM_STATE = 5;
const int NUM_STATE = 6;
static const char *state_names[] = {
"IDLE",
"BUSY",
"EXCLUSIVE",
"MILITARY",
"CHILD",
"OTHER",
@ -133,13 +137,13 @@ static const dwarf_state dwarf_states[] = {
BUSY /* CarveFortification */,
BUSY /* DetailWall */,
BUSY /* DetailFloor */,
BUSY /* Dig */,
BUSY /* CarveUpwardStaircase */,
BUSY /* CarveDownwardStaircase */,
BUSY /* CarveUpDownStaircase */,
BUSY /* CarveRamp */,
BUSY /* DigChannel */,
BUSY /* FellTree */,
EXCLUSIVE /* Dig */,
EXCLUSIVE /* CarveUpwardStaircase */,
EXCLUSIVE /* CarveDownwardStaircase */,
EXCLUSIVE /* CarveUpDownStaircase */,
EXCLUSIVE /* CarveRamp */,
EXCLUSIVE /* DigChannel */,
EXCLUSIVE /* FellTree */,
BUSY /* GatherPlants */,
BUSY /* RemoveConstruction */,
BUSY /* CollectWebs */,
@ -154,7 +158,7 @@ static const dwarf_state dwarf_states[] = {
OTHER /* Sleep */,
BUSY /* CollectSand */,
BUSY /* Fish */,
BUSY /* Hunt */,
EXCLUSIVE /* Hunt */,
OTHER /* HuntVermin */,
BUSY /* Kidnap */,
BUSY /* BeatCriminal */,
@ -183,7 +187,7 @@ static const dwarf_state dwarf_states[] = {
OTHER /* GoShopping2 */,
BUSY /* Clean */,
OTHER /* Rest */,
BUSY /* PickupEquipment */,
EXCLUSIVE /* PickupEquipment */,
BUSY /* DumpItem */,
OTHER /* StrangeMoodCrafter */,
OTHER /* StrangeMoodJeweller */,
@ -393,8 +397,15 @@ struct labor_default
int active_dwarfs;
};
// The percentage of the dwarves assigned as haulers at any one time.
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 const struct labor_default default_labor_infos[] = {
@ -521,7 +532,6 @@ struct dwarf_info_t
bool medical; // this dwarf has medical responsibility
bool trader; // this dwarf has trade responsibility
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)
@ -738,7 +748,13 @@ struct laborinfo_sorter
{
bool operator() (int i,int j)
{
return labor_infos[i].mode() < labor_infos[j].mode();
if (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_value = -10000;
int best_skill = 0;
std::vector<int> values(n_dwarfs);
std::vector<int> candidates;
@ -813,6 +830,9 @@ static void assign_labor(unit_labor::unit_labor labor,
dwarf_skill[dwarf] = skill_level;
dwarf_skillxp[dwarf] = skill_experience;
if (best_skill < skill_level)
best_skill = skill_level;
value += skill_level * 100;
value += skill_experience / 20;
if (skill_level > 0 || skill_experience > 0)
@ -832,6 +852,9 @@ static void assign_labor(unit_labor::unit_labor labor,
value += 350;
}
if (dwarf_info[dwarf].has_exclusive_labor)
value -= 500;
// bias by 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)
min_dwarfs = max_dwarfs = 0;
bool want_idle_dwarf = true;
if (state_count[IDLE] < 2)
want_idle_dwarf = false;
// If there are enough idle dwarves to choose from, enter an aggressive assignment
// mode. "Enough" idle dwarves is defined as 2 or 10% of the total number of dwarves,
// 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
* 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
* - The dwarf has nonzero skill associated with the labor
* - We are in aggressive mode and have not yet assigned an idle dwarf
* - The dwarf is good at this skill
* - The labor is mining, hunting, or woodcutting and the dwarf currently has it enabled.
* 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
@ -917,24 +953,23 @@ static void assign_labor(unit_labor::unit_labor labor,
{
int dwarf = candidates[i];
if (dwarf_info[dwarf].trader && trader_requested)
continue;
if (dwarf_info[dwarf].diplomacy)
continue;
assert(dwarf >= 0);
assert(dwarf < n_dwarfs);
bool preferred_dwarf = false;
if (want_idle_dwarf && dwarf_info[dwarf].state == IDLE)
preferred_dwarf = true;
if (dwarf_skill[dwarf] > 0)
if (dwarf_skillxp[dwarf] > 0 && dwarf_skill[dwarf] >= best_skill / 2)
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;
if (dwarf_info[dwarf].medical && labor == df::unit_labor::DIAGNOSE)
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;
if (!dwarfs[dwarf]->status.labors[labor])
@ -952,11 +987,11 @@ static void assign_labor(unit_labor::unit_labor labor,
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)" : "");
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++;
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++)
{
dwarf_info[dwarf].single_labor = -1;
if (dwarfs[dwarf]->status.souls.size() <= 0)
continue;
@ -1214,7 +1247,9 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
laborinfo_sorter 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)
{
auto labor = *lp;
@ -1224,12 +1259,6 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
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 (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
// 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)
num_haulers = 1;
@ -1274,7 +1303,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
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);
}
dwarfinfo_sorter sorter(dwarf_info);
@ -1305,7 +1334,7 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
dwarfs[dwarf]->status.labors[labor] = true;
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++;
if (print_debug)

@ -203,12 +203,20 @@ for job,flag in pairs(plant_products) do
local itag = 'idx_'..string.lower(flag)
job_outputs[job] = function(callback, job)
local mat_type, mat_index = -1, -1
local seed_type, seed_index = -1, -1
local mat = dfhack.matinfo.decode(job.job_items[0])
if mat and mat.plant and mat.plant.flags[flag] then
mat_type = mat.plant.material_defs[ttag]
mat_index = mat.plant.material_defs[itag]
seed_type = mat.plant.material_defs['type_seed']
seed_index = mat.plant.material_defs['idx_seed']
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