diff --git a/plugins/ruby/README b/plugins/ruby/README index 97a4cbd30..8a473f332 100644 --- a/plugins/ruby/README +++ b/plugins/ruby/README @@ -23,7 +23,7 @@ All ruby code runs while the main DF process and other plugins are suspended. DFHack console -------------- -The ruby plugin defines 1 dfhack console command: +The ruby plugin defines one new dfhack console command: rb_eval ; evaluate a ruby expression and show the result in the console. Ex: rb_eval df.unit_find().name.first_name You can use single-quotes for strings ; avoid double-quotes that are parsed @@ -50,7 +50,7 @@ The script can access the console command arguments through the global variable '$script_args', which is an array of ruby Strings. The help string displayed in dfhack 'ls' command is the first line of the -script, if it is a comment (starts with '# '). +script, if it is a comment (ie starts with '# '). Ruby helper functions @@ -67,7 +67,9 @@ obj1 and 2 should respond to #pos and #x #y #z. Returns the MapBlock for the coordinates or nil. df.map_tile_at(pos) -Returns a MapTile, holds all information relative to the map tile. +Returns a MapTile, holding all informations wrt the map tile (read&write). +This class is a ruby specific extention, to facilitate interaction with the +DF map data. Check out hack/ruby/map.rb. df.each_map_block { |b| } df.each_map_block_z(zlevel) { |b| } @@ -142,9 +144,16 @@ 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 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 +(eg getName). + ex: s = DFHack::StlString.cpp_new ; df.building_find.getName(s) ; p s.str -Deallocation is not supported. You may manually call df.free if you know -what you are doing (maps directly to the native malloc/free) +Deallocation may work, using the compound method _cpp_delete. Use with caution, +may crash your DF session. It may be simpler to just leak the memory. +_cpp_delete will try to free all memory directly used by the compound, eg +strings and vectors. It will *not* call the class destructor, and will not free +stuff behind pointers. C++ std::string fields may be directly re-allocated using standard ruby strings, e.g. some_unit.name.nickname = 'moo' @@ -160,11 +169,13 @@ To delete an element, vector.delete_at(index) You can binary search an element in a vector for a given numeric field value: df.world.unit.all.binsearch(42, :id) -will find the element whose 'id' field is 42 (needs the vector to be initially +will find the entry whose 'id' field is 42 (needs the vector to be initially sorted by this field). The binsearch 2nd argument defaults to :id. Any numeric field defined as being an enum value will be converted to a ruby Symbol. This works for array indexes too. + ex: df.unit_find(:selected).status.labors[:HAUL_FOOD] = true + df.map_tile_at(df.cursor).designation.liquid_type = :Water Virtual method calls are supported for C++ objects, with a maximum of 4 arguments. Arguments / return value are interpreted as Compound/Enums as @@ -194,7 +205,7 @@ Change current unit profession Center the screen on unit ID '123' df.center_viewscreen(df.unit_find(123)) -Find an item at a given position, show its C++ classname +Find an item under the game cursor and show its C++ classname p df.item_find(df.cursor)._rtti_classname Find the raws name of the plant under cursor @@ -205,15 +216,29 @@ Dig a channel under the cursor df.map_designation_at(df.cursor).dig = :Channel df.map_block_at(df.cursor).flags.designated = true +Spawn 2/7 magma on the tile of the dwarf nicknamed 'hotfeet' + hot = df.unit_citizens.find { |u| u.name.nickname == 'hotfeet' } + df.map_tile_at(hot).spawn_magma(2) + Plugin compilation ------------------ -The plugin consists of the *.rb file including user comfort functions and -describing basic classes used by the autogenerated code, and ruby-autogen.rb, -the auto-generated code. +The plugin consists of the main ruby.cpp native plugin and the *.rb files. + +The native plugin handles only low-level ruby-to-df interaction (eg raw memory +read/write, and dfhack integration), and the .rb files hold end-user helper +functions. + +On dfhack start, the native plugin will initialize the ruby interpreter, and +load hack/ruby/ruby.rb. This one then loads all other .rb files. -autogen is output by codegen.pl from dfhack/library/include/df/codegen.out.xml +The DF internal structures are described in ruby-autogen.rb . +It is output by ruby/codegen.pl, from dfhack/library/include/df/codegen.out.xml +It contains architecture-specific data (eg DF internal structures field offsets, +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, @@ -230,6 +255,7 @@ Will generate The syntax for the 'field' method in ruby-autogen.rb is: 1st argument = name of the method 2nd argument = offset of this field from the beginning of the current struct. + This field depends on the compiler used by Toady to generate DF. The block argument describes the type of the field: uint32, ptr to global... Primitive type access is done through native methods from ruby.cpp (vector length, diff --git a/plugins/ruby/ruby-autogen-defs.rb b/plugins/ruby/ruby-autogen-defs.rb index 2e4948acb..7165e2448 100644 --- a/plugins/ruby/ruby-autogen-defs.rb +++ b/plugins/ruby/ruby-autogen-defs.rb @@ -7,6 +7,7 @@ module DFHack def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end def _get ; self ; end def _cpp_init ; end + def _cpp_delete ; end end class Compound < MemStruct @@ -114,6 +115,11 @@ module DFHack def _cpp_init _fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_init } end + def _cpp_delete + _fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_delete } + DFHack.free(@_memaddr) + @_memaddr = nil # turn future segfaults in harmless ruby exceptions + end def _set(h) case h when Hash; h.each { |k, v| send("#{k}=", v) } @@ -372,6 +378,9 @@ module DFHack def _cpp_init _length.times { |i| _tgat(i)._cpp_init } end + def _cpp_delete + _length.times { |i| _tgat(i)._cpp_delete } + end alias length _length alias size _length def _tgat(i) @@ -423,10 +432,10 @@ module DFHack DFHack.memory_vector32_ptrat(@_memaddr, idx) end def insert_at(idx, val) - DFHack.memory_vector32_insert(@_memaddr, idx, val) + DFHack.memory_vector32_insertat(@_memaddr, idx, val) end def delete_at(idx) - DFHack.memory_vector32_delete(@_memaddr, idx) + DFHack.memory_vector32_deleteat(@_memaddr, idx) end def _set(v) @@ -434,6 +443,12 @@ module DFHack v.each_with_index { |e, i| self[i] = e } # patch entries end + def self._cpp_new + new._at DFHack.memory_vector_new + end + def _cpp_delete + DFHack.memory_vector_delete(@_memaddr) + end def _cpp_init DFHack.memory_vector_init(@_memaddr) end @@ -496,10 +511,10 @@ module DFHack DFHack.memory_vector16_ptrat(@_memaddr, idx) end def insert_at(idx, val) - DFHack.memory_vector16_insert(@_memaddr, idx, val) + DFHack.memory_vector16_insertat(@_memaddr, idx, val) end def delete_at(idx) - DFHack.memory_vector16_delete(@_memaddr, idx) + DFHack.memory_vector16_deleteat(@_memaddr, idx) end end class StlVector8 < StlVector32 @@ -510,10 +525,10 @@ module DFHack DFHack.memory_vector8_ptrat(@_memaddr, idx) end def insert_at(idx, val) - DFHack.memory_vector8_insert(@_memaddr, idx, val) + DFHack.memory_vector8_insertat(@_memaddr, idx, val) end def delete_at(idx) - DFHack.memory_vector8_delete(@_memaddr, idx) + DFHack.memory_vector8_deleteat(@_memaddr, idx) end end class StlBitVector < StlVector32 @@ -522,10 +537,10 @@ module DFHack DFHack.memory_vectorbool_length(@_memaddr) end def insert_at(idx, val) - DFHack.memory_vectorbool_insert(@_memaddr, idx, val) + DFHack.memory_vectorbool_insertat(@_memaddr, idx, val) end def delete_at(idx) - DFHack.memory_vectorbool_delete(@_memaddr, idx) + DFHack.memory_vectorbool_deleteat(@_memaddr, idx) end def [](idx) idx += length if idx < 0 @@ -541,6 +556,12 @@ module DFHack DFHack.memory_vectorbool_setat(@_memaddr, idx, v) end end + def self._cpp_new + new._at DFHack.memory_vectorbool_new + end + def _cpp_delete + DFHack.memory_vectorbool_delete(@_memaddr) + end end class StlString < MemStruct def _get @@ -551,6 +572,12 @@ module DFHack DFHack.memory_write_stlstring(@_memaddr, v) end + def self._cpp_new + new._at DFHack.memory_stlstring_new + end + def _cpp_delete + DFHack.memory_stlstring_delete(@_memaddr) + end def _cpp_init DFHack.memory_stlstring_init(@_memaddr) end @@ -574,7 +601,7 @@ module DFHack def length DFHack.memory_bitarray_length(@_memaddr) end - # TODO _cpp_init + # TODO _cpp_init, _cpp_delete def size ; length ; end def resize(len) DFHack.memory_bitarray_resize(@_memaddr, len) @@ -608,7 +635,7 @@ module DFHack def length ; _length ; end def size ; _length ; end - # TODO _cpp_init + # TODO _cpp_init, _cpp_delete def _tgat(i) @_tg._at(_ptr + i*@_tglen) if i >= 0 and i < _length end @@ -702,6 +729,21 @@ module DFHack def self.sym(v) ; (!v || (v == 0)) ? false : true ; end end + class StlString < MemHack::Compound + field(:str, 0) { stl_string } + + def self.cpp_new(init=nil) + s = MemHack::StlString._cpp_new + s._set(init) if init + new._at(s._memaddr) + end + + def _cpp_delete + MemHack::StlString.new._at(@_memaddr+0)._cpp_delete + @_memaddr = nil + end + end + # cpp rtti name -> rb class @rtti_n2c = {} @rtti_c2n = {} diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index 08ea13b9f..1391faa44 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -528,7 +528,7 @@ static VALUE rb_dfmalloc(VALUE self, VALUE len) if (!ptr) return Qnil; memset(ptr, 0, FIX2INT(len)); - return rb_uint2inum((long)ptr); + return rb_uint2inum((uint32_t)ptr); } static VALUE rb_dffree(VALUE self, VALUE ptr) @@ -599,6 +599,18 @@ static VALUE rb_dfmemory_write_float(VALUE self, VALUE addr, VALUE val) // stl::string +static VALUE rb_dfmemory_stlstring_new(VALUE self) +{ + std::string *ptr = new std::string; + return rb_uint2inum((uint32_t)ptr); +} +static VALUE rb_dfmemory_stlstring_delete(VALUE self, VALUE addr) +{ + std::string *ptr = (std::string*)rb_num2ulong(addr); + if (ptr) + delete ptr; + return Qtrue; +} static VALUE rb_dfmemory_stlstring_init(VALUE self, VALUE addr) { // XXX THIS IS TERRIBLE @@ -621,6 +633,18 @@ static VALUE rb_dfmemory_write_stlstring(VALUE self, VALUE addr, VALUE val) // vector access +static VALUE rb_dfmemory_vec_new(VALUE self) +{ + std::vector *ptr = new std::vector; + return rb_uint2inum((uint32_t)ptr); +} +static VALUE rb_dfmemory_vec_delete(VALUE self, VALUE addr) +{ + std::vector *ptr = (std::vector*)rb_num2ulong(addr); + if (ptr) + delete ptr; + return Qtrue; +} static VALUE rb_dfmemory_vec_init(VALUE self, VALUE addr) { std::vector *ptr = new std::vector; @@ -638,13 +662,13 @@ static VALUE rb_dfmemory_vec8_ptrat(VALUE self, VALUE addr, VALUE idx) std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } -static VALUE rb_dfmemory_vec8_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +static VALUE rb_dfmemory_vec8_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); return Qtrue; } -static VALUE rb_dfmemory_vec8_delete(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vec8_deleteat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->erase(v->begin()+FIX2INT(idx)); @@ -662,13 +686,13 @@ static VALUE rb_dfmemory_vec16_ptrat(VALUE self, VALUE addr, VALUE idx) std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } -static VALUE rb_dfmemory_vec16_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +static VALUE rb_dfmemory_vec16_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); return Qtrue; } -static VALUE rb_dfmemory_vec16_delete(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vec16_deleteat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->erase(v->begin()+FIX2INT(idx)); @@ -686,13 +710,13 @@ static VALUE rb_dfmemory_vec32_ptrat(VALUE self, VALUE addr, VALUE idx) std::vector *v = (std::vector*)rb_num2ulong(addr); return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx))); } -static VALUE rb_dfmemory_vec32_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +static VALUE rb_dfmemory_vec32_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val)); return Qtrue; } -static VALUE rb_dfmemory_vec32_delete(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vec32_deleteat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->erase(v->begin()+FIX2INT(idx)); @@ -700,6 +724,24 @@ static VALUE rb_dfmemory_vec32_delete(VALUE self, VALUE addr, VALUE idx) } // vector +static VALUE rb_dfmemory_vecbool_new(VALUE self) +{ + std::vector *ptr = new std::vector; + return rb_uint2inum((uint32_t)ptr); +} +static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr) +{ + std::vector *ptr = (std::vector*)rb_num2ulong(addr); + if (ptr) + delete ptr; + return Qtrue; +} +static VALUE rb_dfmemory_vecbool_init(VALUE self, VALUE addr) +{ + std::vector *ptr = new std::vector; + memcpy((void*)rb_num2ulong(addr), (void*)ptr, sizeof(*ptr)); + return Qtrue; +} static VALUE rb_dfmemory_vecbool_length(VALUE self, VALUE addr) { std::vector *v = (std::vector*)rb_num2ulong(addr); @@ -716,13 +758,13 @@ static VALUE rb_dfmemory_vecbool_setat(VALUE self, VALUE addr, VALUE idx, VALUE v->at(FIX2INT(idx)) = (BOOL_ISFALSE(val) ? 0 : 1); return Qtrue; } -static VALUE rb_dfmemory_vecbool_insert(VALUE self, VALUE addr, VALUE idx, VALUE val) +static VALUE rb_dfmemory_vecbool_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->insert(v->begin()+FIX2INT(idx), (BOOL_ISFALSE(val) ? 0 : 1)); return Qtrue; } -static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr, VALUE idx) +static VALUE rb_dfmemory_vecbool_deleteat(VALUE self, VALUE addr, VALUE idx) { std::vector *v = (std::vector*)rb_num2ulong(addr); v->erase(v->begin()+FIX2INT(idx)); @@ -834,27 +876,34 @@ static void ruby_bind_dfhack(void) { rb_define_singleton_method(rb_cDFHack, "memory_write_int32", RUBY_METHOD_FUNC(rb_dfmemory_write_int32), 2); rb_define_singleton_method(rb_cDFHack, "memory_write_float", RUBY_METHOD_FUNC(rb_dfmemory_write_float), 2); + rb_define_singleton_method(rb_cDFHack, "memory_stlstring_new", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_new), 0); + rb_define_singleton_method(rb_cDFHack, "memory_stlstring_delete", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_delete), 1); rb_define_singleton_method(rb_cDFHack, "memory_stlstring_init", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_init), 1); rb_define_singleton_method(rb_cDFHack, "memory_read_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_read_stlstring), 1); rb_define_singleton_method(rb_cDFHack, "memory_write_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_write_stlstring), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector_new", RUBY_METHOD_FUNC(rb_dfmemory_vec_new), 0); + rb_define_singleton_method(rb_cDFHack, "memory_vector_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec_delete), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector_init", RUBY_METHOD_FUNC(rb_dfmemory_vec_init), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector8_length", RUBY_METHOD_FUNC(rb_dfmemory_vec8_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector8_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_ptrat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector8_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec8_insert), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vector8_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec8_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector8_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_insertat), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector8_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_deleteat), 2); rb_define_singleton_method(rb_cDFHack, "memory_vector16_length", RUBY_METHOD_FUNC(rb_dfmemory_vec16_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector16_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_ptrat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector16_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec16_insert), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vector16_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec16_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector16_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_insertat), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector16_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_deleteat), 2); rb_define_singleton_method(rb_cDFHack, "memory_vector32_length", RUBY_METHOD_FUNC(rb_dfmemory_vec32_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_vector32_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_ptrat), 2); - rb_define_singleton_method(rb_cDFHack, "memory_vector32_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec32_insert), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vector32_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec32_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vector32_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_insertat), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vector32_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_deleteat), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_new", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_new), 0); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_delete", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_delete), 1); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_init", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_init), 1); rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_length", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_at", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_at), 2); rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_setat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_setat), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_insert", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_insert), 3); - rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_delete", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_delete), 2); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_insertat), 3); + rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_deleteat), 2); rb_define_singleton_method(rb_cDFHack, "memory_bitarray_length", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_length), 1); rb_define_singleton_method(rb_cDFHack, "memory_bitarray_resize", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_resize), 2); rb_define_singleton_method(rb_cDFHack, "memory_bitarray_isset", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_isset), 2);