Implement map load/unload scripts and allow additional scripts to be registered

develop
lethosor 2015-02-07 19:33:12 -05:00
parent 3793d87fba
commit 56dfc66309
2 changed files with 186 additions and 6 deletions

@ -419,6 +419,42 @@ string findScript(string path, string name) {
return "";
}
static std::map<std::string, state_change_event> state_change_event_map;
static void sc_event_map_init() {
if (!state_change_event_map.size())
{
#define insert(name) state_change_event_map.insert(std::pair<std::string, state_change_event>(#name, name))
insert(SC_WORLD_LOADED);
insert(SC_WORLD_UNLOADED);
insert(SC_MAP_LOADED);
insert(SC_MAP_UNLOADED);
insert(SC_VIEWSCREEN_CHANGED);
insert(SC_PAUSED);
insert(SC_UNPAUSED);
#undef insert
}
}
static state_change_event sc_event_id (std::string name) {
sc_event_map_init();
auto it = state_change_event_map.find(name);
if (it != state_change_event_map.end())
return it->second;
if (name.find("SC_") != 0)
return sc_event_id(std::string("SC_") + name);
return SC_UNKNOWN;
}
static std::string sc_event_name (state_change_event id) {
sc_event_map_init();
for (auto it = state_change_event_map.begin(); it != state_change_event_map.end(); ++it)
{
if (it->second == id)
return it->first;
}
return "SC_UNKNOWN";
}
command_result Core::runCommand(color_ostream &con, const std::string &first, vector<string> &parts)
{
if (!first.empty())
@ -661,6 +697,7 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
" die - Force DF to close immediately\n"
" keybinding - Modify bindings of commands to keys\n"
" script FILENAME - Run the commands specified in a file.\n"
" sc-script - Automatically run specified scripts on state change events\n"
" plug [PLUGIN|v] - List plugin state and detailed description.\n"
" load PLUGIN|all - Load a plugin by name or load all possible plugins.\n"
" unload PLUGIN|all - Unload a plugin or all loaded plugins.\n"
@ -806,6 +843,104 @@ command_result Core::runCommand(color_ostream &con, const std::string &first, ve
}
return CR_OK;
}
else if(first == "sc-script")
{
if (parts.size() < 1)
{
con << "Usage: sc-script add|remove|list|help SC_EVENT [path-to-script] [...]" << endl;
return CR_WRONG_USAGE;
}
if (parts[0] == "help" || parts[0] == "?")
{
con << "Valid event names (SC_ prefix is optional):" << endl;
for (int i = SC_WORLD_LOADED; i <= SC_UNPAUSED; i++)
{
std::string name = sc_event_name((state_change_event)i);
if (name != "SC_UNKNOWN")
con << " " << name << endl;
}
return CR_OK;
}
else if (parts[0] == "list")
{
if(parts.size() < 2)
parts.push_back("");
if (parts[1].size() && sc_event_id(parts[1]) == SC_UNKNOWN)
{
con << "Unrecognized event name: " << parts[1] << endl;
return CR_WRONG_USAGE;
}
for (auto it = state_change_scripts.begin(); it != state_change_scripts.end(); ++it)
{
if (!parts[1].size() || (it->event == sc_event_id(parts[1])))
{
con.print("%s (%s): %s%s\n", sc_event_name(it->event).c_str(),
it->save_specific ? "save-specific" : "global",
it->save_specific ? "<save folder>/raw/" : "<DF folder>/",
it->path.c_str());
}
}
return CR_OK;
}
else if (parts[0] == "add")
{
if (parts.size() < 3 || (parts.size() >= 4 && parts[3] != "-save"))
{
con << "Usage: sc-script add EVENT path-to-script [-save]" << endl;
return CR_WRONG_USAGE;
}
state_change_event evt = sc_event_id(parts[1]);
if (evt == SC_UNKNOWN)
{
con << "Unrecognized event: " << parts[1] << endl;
return CR_FAILURE;
}
bool save_specific = (parts.size() >= 4 && parts[3] == "-save");
StateChangeScript script(evt, parts[2], save_specific);
for (auto it = state_change_scripts.begin(); it != state_change_scripts.end(); ++it)
{
if (script == *it)
{
con << "Script already registered" << endl;
return CR_FAILURE;
}
}
state_change_scripts.push_back(script);
return CR_OK;
}
else if (parts[0] == "remove")
{
if (parts.size() < 3 || (parts.size() >= 4 && parts[3] != "-save"))
{
con << "Usage: sc-script remove EVENT path-to-script [-save]" << endl;
return CR_WRONG_USAGE;
}
state_change_event evt = sc_event_id(parts[1]);
if (evt == SC_UNKNOWN)
{
con << "Unrecognized event: " << parts[1] << endl;
return CR_FAILURE;
}
bool save_specific = (parts.size() >= 4 && parts[3] == "-save");
StateChangeScript tmp(evt, parts[2], save_specific);
auto it = std::find(state_change_scripts.begin(), state_change_scripts.end(), tmp);
if (it != state_change_scripts.end())
{
state_change_scripts.erase(it);
return CR_OK;
}
else
{
con << "Unrecognized script" << endl;
return CR_FAILURE;
}
}
else
{
con << "Usage: sc-script add|remove|list|help SC_EVENT [path-to-script] [...]" << endl;
return CR_WRONG_USAGE;
}
}
else
{
command_result res = plug_mgr->InvokeCommand(con, first, parts);
@ -1464,7 +1599,7 @@ void Core::onUpdate(color_ostream &out)
Lua::Core::onUpdate(out);
}
static void handleLoadAndUnloadScripts(Core* core, color_ostream& out, state_change_event event) {
void Core::handleLoadAndUnloadScripts(color_ostream& out, state_change_event event) {
if (!df::global::world)
return;
//TODO: use different separators for windows
@ -1476,16 +1611,42 @@ static void handleLoadAndUnloadScripts(Core* core, color_ostream& out, state_cha
std::string rawFolder = "data" + separator + "save" + separator + (df::global::world->cur_savegame.save_dir) + separator + "raw" + separator;
switch(event) {
case SC_WORLD_LOADED:
core->loadScriptFile(out, "onLoadWorld.init", true);
core->loadScriptFile(out, rawFolder + "onLoad.init", true);
loadScriptFile(out, "onLoadWorld.init", true);
loadScriptFile(out, rawFolder + "onLoadWorld.init", true);
loadScriptFile(out, rawFolder + "onLoad.init", true);
break;
case SC_WORLD_UNLOADED:
core->loadScriptFile(out, "onUnloadWorld.init", true);
core->loadScriptFile(out, rawFolder + "onUnload.init", true);
loadScriptFile(out, "onUnloadWorld.init", true);
loadScriptFile(out, rawFolder + "onUnloadWorld.init", true);
loadScriptFile(out, rawFolder + "onUnload.init", true);
break;
case SC_MAP_LOADED:
loadScriptFile(out, "onLoadMap.init", true);
loadScriptFile(out, rawFolder + "onLoadMap.init", true);
break;
case SC_MAP_UNLOADED:
loadScriptFile(out, "onUnloadMap.init", true);
loadScriptFile(out, rawFolder + "onUnloadMap.init", true);
break;
default:
break;
}
for (auto it = state_change_scripts.begin(); it != state_change_scripts.end(); ++it)
{
if (it->event == event)
{
if (!it->save_specific)
{
if (!loadScriptFile(out, it->path, true))
out.printerr("Could not load script: %s\n", it->path.c_str());
}
else if (it->save_specific && isWorldLoaded())
{
loadScriptFile(out, rawFolder + it->path, true);
}
}
}
}
void Core::onStateChange(color_ostream &out, state_change_event event)
@ -1498,7 +1659,7 @@ void Core::onStateChange(color_ostream &out, state_change_event event)
Lua::Core::onStateChange(out, event);
handleLoadAndUnloadScripts(this, out, event);
handleLoadAndUnloadScripts(out, event);
}
// FIXME: needs to terminate the IO threads and properly dismantle all the machinery involved.

@ -72,6 +72,7 @@ namespace DFHack
enum state_change_event
{
SC_UNKNOWN = -1,
SC_WORLD_LOADED = 0,
SC_WORLD_UNLOADED = 1,
SC_MAP_LOADED = 2,
@ -83,6 +84,21 @@ namespace DFHack
SC_UNPAUSED = 8
};
class DFHACK_EXPORT StateChangeScript
{
public:
state_change_event event;
std::string path;
bool save_specific;
StateChangeScript(state_change_event event, std::string path, bool save_specific = false)
:event(event), path(path), save_specific(save_specific)
{ }
bool operator==(const StateChangeScript& other)
{
return event == other.event && path == other.path && save_specific == other.save_specific;
}
};
// Core is a singleton. Why? Because it is closely tied to SDL calls. It tracks the global state of DF.
// There should never be more than one instance
// Better than tracking some weird variables all over the place.
@ -190,6 +206,7 @@ namespace DFHack
void doUpdate(color_ostream &out, bool first_update);
void onUpdate(color_ostream &out);
void onStateChange(color_ostream &out, state_change_event event);
void handleLoadAndUnloadScripts(color_ostream &out, state_change_event event);
Core(Core const&); // Don't Implement
void operator=(Core const&); // Don't implement
@ -240,6 +257,8 @@ namespace DFHack
bool last_pause_state;
// Very important!
bool started;
// Additional state change scripts
std::vector<StateChangeScript> state_change_scripts;
tthread::mutex * misc_data_mutex;
std::map<std::string,void*> misc_data_map;