This plugins embeds a ruby interpreter inside DFHack (ie inside Dwarf Fortress). The plugin maps all the structures available in library/xml/ to ruby objects. These objects are described in ruby-autogen.rb, they are all in the DFHack:: module. The toplevel 'df' method returs the DFHack module. The plugin does *not* map most of dfhack methods (MapCache, ...) ; only direct access to the raw DF data structures in memory is provided. Some library methods are stored in the ruby.rb file, with shortcuts to read a map block, find an unit or an item, etc. Global objects are stored in the GlobalObjects class ; each object accessor is mirrored as a DFHack module method. The ruby plugin defines 2 dfhack console commands: rb_load ; load a ruby script. Ex: rb_load hack/plants.rb (no quotes) rb_eval ; evaluate a ruby expression, show the result in the console. Ex: rb_eval df.find_unit.name.first_name You can use single-quotes for strings ; avoid double-quotes that are parsed and removed by the dfhack console. The plugin also interfaces with dfhack 'onupdate' hook. To register ruby code to be run every graphic frame, use: handle = df.onupdate_register { puts 'i love flood' } To stop being called, use: df.onupdate_unregister handle Exemples -------- For more complex exemples, check the ruby/plugins/ folder. Show info on the currently selected unit ('v' or 'k' DF menu) p df.find_unit.flags1 Set a custom nickname to unit with id '123' df.find_unit(123).name.nickname = 'moo' Show current unit profession p df.find_unit.profession Center the screen on unit '123' df.center_viewscreen(df.find_unit(123)) Find an item at a given position, show its C++ classname df.find_item(df.cursor)._rtti_classname Find the raws name of the plant under cursor plant = df.world.plants.all.find { |p| df.at_cursor?(p) } df.world.raws.plants.all[plant.mat_index].id Dig a channel under the cursor df.map_designation_at(df.cursor).dig = TileDigDesignation::Channel df.map_block_at(df.cursor).flags.designated = true Compilation ----------- The plugin consists of the ruby.rb file including user comfort functions ; ruby-memstruct.rb describing basic classes used by the autogenerated code, and embedded at the beginnig of ruby-autogen.rb, and the generated code. The generated code is generated by codegen.pl, which takes the codegen.out.xml file as input. One of the limitations of the xml file is that it does not include structure offsets, as they depend on the compiler. To overcome that, codegen runs in two passes. The first pass generates a ruby-autogen.cpp file, that will output the structure offsets ; the second pass will generate the ruby-autogen.rb using the output of the compiled ruby-autogen.cpp. For exemple, from We generate the cpp printf("%s = %d", "offsetof(df::unit, language_name)", offsetof(df::unit, language_name)); printf("%s = %d", "offsetof(df::unit, custom_profession)", offsetof(df::unit, custom_profession)); printf("%s = %d", "offsetof(df::unit, profession)", offsetof(df::unit, profession)); Which generates (on linux) offsetof(df::unit, name) = 0 offsetof(df::unit, custom_profession) = 60 offsetof(df::unit, profession) = 64 Which generates class Unit < MemHack::Compound field(:name, 0) { global :LanguageName } field(:custom_profession, 60) { stl_string } field(:profession, 64) { number 16, true } The field method has 2 arguments: the name of the method and the member offset ; the block specifies the member type. See ruby-memstruct.rb for more information. Primitive type access is done through native methods in ruby.cpp (vector length, raw memory access, etc) MemHack::Pointers are automatically dereferenced ; so a vector of pointer to Units will yield Units directly. Null pointers yield the 'nil' value. This allows to use code such as 'df.world.units.all[0].pos', with 'all' being really a vector of pointer. Todo ---- Correct c++ object (de)allocation (call ctor etc) ; ability to call vtable methods