diff --git a/plugins/ruby/README b/plugins/ruby/README index 9bead57d4..767844414 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -54,6 +54,19 @@ The help string displayed in dfhack 'ls' command is the first line of the script, if it is a comment (ie starts with '# '). +Calling dfhack commands +----------------------- + +The ruby plugin allows the calling of arbitrary dfhack commands, as if typed +directly on the dfhack prompt. +However due to locks and stuff, the dfhack command is delayed until the current +ruby command is finished, so it is restricted to interactive uses. +It is possible to call the method many times, this will queue dfhack commands +to be run in order. + + df.dfhack_run "reveal" + + Ruby helper functions --------------------- @@ -107,7 +120,7 @@ eg 'gob' for 'GOBLIN' or 'coal' for 'COAL_BITUMINOUS', hence the name. df.building_construct(bld, item_list) Allocates a new building in DF memory, define its position / dimensions, and create a dwarf job to construct it from the given list of items. -See buildings.rb/buildbed for an exemple. +See buildings.rb/buildbed for an example. df.each_tree(material) { |t| } Iterates over every tree of the given material (eg 'maple'). @@ -137,13 +150,17 @@ To stop being called, use: The same mechanism is available for 'onstatechange', but the SC_BEGIN_UNLOAD event is not propagated to the ruby handler. +Available states: + :WORLD_LOADED, :WORLD_UNLOADED, :MAP_LOADED, :MAP_UNLOADED, + :VIEWSCREEN_CHANGED, :CORE_INITIALIZED, :PAUSED, :UNPAUSED + C++ object manipulation ----------------------- The ruby classes defined in ruby-autogen.rb are accessors to the underlying df C++ objects in-memory. To allocate a new C++ object for use in DF, use the -RubyClass.cpp_new method (see buildings.rb for exemples), works for Compounds +RubyClass.cpp_new method (see buildings.rb for examples), works for Compounds only. A special Compound DFHack::StlString is available for allocating a single c++ stl::string, so that you can call vmethods that take a string pointer argument @@ -186,10 +203,10 @@ Pointer fields are automatically dereferenced ; so a vector of pointer to Units will yield Units directly. NULL pointers yield the 'nil' value. -Exemples +Examples -------- -For more complex exemples, check the dfhack/scripts/*.rb files. +For more complex examples, check the dfhack/scripts/*.rb files. Show info on the currently selected unit ('v' or 'k' DF menu) p df.unit_find.flags1 @@ -240,7 +257,7 @@ which differ between Windows and Linux. Linux and Macosx are the same, as they both use gcc). It is stored inside the build directory (eg build/plugins/ruby/ruby-autogen.rb) -For exemple, +For example, diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 187b90e3d..44d4fd240 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -41,6 +41,7 @@ static tthread::thread *r_thread; static int onupdate_active; static int onupdate_minyear, onupdate_minyeartick=-1, onupdate_minyeartickadv=-1; static color_ostream_proxy *console_proxy; +static std::vector *dfhack_run_queue; DFHACK_PLUGIN("ruby") @@ -63,6 +64,9 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector ; + r_type = RB_INIT; // create the dedicated ruby thread @@ -111,6 +115,7 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) // we can release m_mutex, other users will check r_thread m_mutex->unlock(); delete m_mutex; + delete dfhack_run_queue; // dlclose libruby df_unloadruby(); @@ -152,6 +157,8 @@ static command_result do_plugin_eval_ruby(color_ostream &out, const char *comman // send a single ruby line to be evaluated by the ruby thread DFhackCExport command_result plugin_eval_ruby( color_ostream &out, const char *command) { + command_result ret; + // if dlopen failed if (!r_thread) return CR_FAILURE; @@ -160,14 +167,24 @@ DFhackCExport command_result plugin_eval_ruby( color_ostream &out, const char *c // debug only! // run ruby commands without locking the main thread // useful when the game is frozen after a segfault - return do_plugin_eval_ruby(out, command+7); + ret = do_plugin_eval_ruby(out, command+7); } else { // wrap all ruby code inside a suspend block // if we dont do that and rely on ruby code doing it, we'll deadlock in // onupdate CoreSuspender suspend; - return do_plugin_eval_ruby(out, command); + ret = do_plugin_eval_ruby(out, command); } + + // if any dfhack command is queued for run, do it now + while (!dfhack_run_queue->empty()) { + std::string cmd = dfhack_run_queue->at(0); + // delete before running the command, which may be ruby and cause infinite loops + dfhack_run_queue->erase(dfhack_run_queue->begin()); + Core::getInstance().runCommand(out, cmd); + } + + return ret; } DFhackCExport command_result plugin_onupdate ( color_ostream &out ) @@ -550,18 +567,10 @@ static VALUE rb_dfget_vtable_ptr(VALUE self, VALUE objptr) // run a dfhack command, as if typed from the dfhack console static VALUE rb_dfhack_run(VALUE self, VALUE cmd) { - if (!r_console) // XXX - return Qnil; - std::string s; int strlen = FIX2INT(rb_funcall(cmd, rb_intern("length"), 0)); s.assign(rb_string_value_ptr(&cmd), strlen); - - // allow the target command to suspend - // FIXME - //CoreSuspendClaimer suspend(true); - Core::getInstance().runCommand(*r_console, s); - + dfhack_run_queue->push_back(s); return Qtrue; } @@ -1045,7 +1054,7 @@ static void ruby_bind_dfhack(void) { rb_define_singleton_method(rb_cDFHack, "get_vtable", RUBY_METHOD_FUNC(rb_dfget_vtable), 1); rb_define_singleton_method(rb_cDFHack, "get_rtti_classname", RUBY_METHOD_FUNC(rb_dfget_rtti_classname), 1); rb_define_singleton_method(rb_cDFHack, "get_vtable_ptr", RUBY_METHOD_FUNC(rb_dfget_vtable_ptr), 1); - //rb_define_singleton_method(rb_cDFHack, "dfhack_run", RUBY_METHOD_FUNC(rb_dfhack_run), 1); + rb_define_singleton_method(rb_cDFHack, "dfhack_run", RUBY_METHOD_FUNC(rb_dfhack_run), 1); rb_define_singleton_method(rb_cDFHack, "print_str", RUBY_METHOD_FUNC(rb_dfprint_str), 1); rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1); rb_define_singleton_method(rb_cDFHack, "malloc", RUBY_METHOD_FUNC(rb_dfmalloc), 1);