ruby: README
parent
bf64c3e23c
commit
d1ea97c272
@ -0,0 +1,116 @@
|
||||
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 <filename> ; load a ruby script. Ex: rb_load hack/plants.rb (no quotes)
|
||||
rb_eval <ruby expression> ; 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 DFHack::Profession::ENUM[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
|
||||
<ld:global-type ld:meta="struct-type" type-name="unit">
|
||||
<ld:field type-name="language_name" name="name" ld:meta="global"/>
|
||||
<ld:field name="custom_profession" ld:meta="primitive" ld:subtype="stl-string"/>
|
||||
<ld:field ld:subtype="enum" base-type="int16_t" name="profession" type-name="profession" ld:meta="global"/>
|
||||
|
||||
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 have a special behavior: they accept and forward any method to
|
||||
the pointed object. To retrieve the pointed object directly, use the _getv
|
||||
method. Null pointers resolve to 'nil'.
|
||||
Beware, invalid pointers (!= 0) will crash the plugin and the game.
|
||||
|
||||
This allows to use code such as 'df.world.units.all[0].pos', with all[0] being
|
||||
really a Pointer (with no 'pos' method).
|
||||
|
||||
|
||||
Todo
|
||||
----
|
||||
|
||||
New C++ object allocation ; update vector/compound fields with pointers ; bind
|
||||
onstatechange
|
Loading…
Reference in New Issue