ruby: fix plugin/plant.rb, add onstatechange hook, use console proxy, general cleanup, enable build by default

develop
jj 2012-04-25 20:21:09 +02:00
parent bd2e6e74de
commit 7a8db179ab
5 changed files with 122 additions and 123 deletions

@ -36,7 +36,7 @@ if (BUILD_DWARFEXPORT)
add_subdirectory (dwarfexport)
endif()
OPTION(BUILD_RUBY "Build ruby binding." OFF)
OPTION(BUILD_RUBY "Build ruby binding." ON)
if (BUILD_RUBY)
add_subdirectory (ruby)
endif()

@ -112,5 +112,4 @@ really a Pointer (with no 'pos' method).
Todo
----
New C++ object allocation ; update vector/compound fields with pointers ; bind
onstatechange
Correct c++ object (de)allocation (call ctor etc)

@ -42,10 +42,12 @@ def self.cuttrees(material=nil, count_max=100)
if !material
# list trees
cnt = Hash.new(0)
each_tree { |plant|
next if plant.grow_counter < SaplingToTreeAge
next if find_map_designation(plant).hidden
cnt[plant.material] += 1
suspend {
each_tree { |plant|
next if plant.grow_counter < SaplingToTreeAge
next if map_designation_at(plant).hidden
cnt[plant.material] += 1
}
}
cnt.sort_by { |mat, c| c }.each { |mat, c|
name = @raws_tree_name[mat]
@ -53,17 +55,19 @@ def self.cuttrees(material=nil, count_max=100)
}
else
cnt = 0
each_tree(material) { |plant|
next if plant.grow_counter < SaplingToTreeAge
b = find_map_block(plant)
d = b.designation[plant.pos.x%16][plant.pos.y%16]
next if d.hidden
if d.dig == TileDigDesignation::No
d.dig = TileDigDesignation::Default
b.flags.designated = true
cnt += 1
break if cnt == count_max
end
suspend {
each_tree(material) { |plant|
next if plant.grow_counter < SaplingToTreeAge
b = map_block_at(plant)
d = b.designation[plant.pos.x%16][plant.pos.y%16]
next if d.hidden
if d.dig == TileDigDesignation::No
d.dig = TileDigDesignation::Default
b.flags.designated = true
cnt += 1
break if cnt == count_max
end
}
}
puts "Updated #{cnt} plant designations"
end
@ -73,10 +77,12 @@ def self.growtrees(material=nil, count_max=100)
if !material
# list plants
cnt = Hash.new(0)
each_tree { |plant|
next if plant.grow_counter >= SaplingToTreeAge
next if find_map_designation(plant).hidden
cnt[plant.material] += 1
suspend {
each_tree { |plant|
next if plant.grow_counter >= SaplingToTreeAge
next if map_designation_at(plant).hidden
cnt[plant.material] += 1
}
}
cnt.sort_by { |mat, c| c }.each { |mat, c|
name = @raws_tree_name[mat]
@ -84,12 +90,14 @@ def self.growtrees(material=nil, count_max=100)
}
else
cnt = 0
each_tree(material) { |plant|
next if plant.grow_counter >= SaplingToTreeAge
next if find_map_designation(plant).hidden
p.grow_counter = SaplingToTreeAge
cnt += 1
break if cnt == count_max
suspend {
each_tree(material) { |plant|
next if plant.grow_counter >= SaplingToTreeAge
next if map_designation_at(plant).hidden
plant.grow_counter = SaplingToTreeAge
cnt += 1
break if cnt == count_max
}
}
puts "Grown #{cnt} saplings"
end
@ -107,11 +115,13 @@ def self.growcrops(material=nil, count_max=100)
if !material
cnt = Hash.new(0)
world.items.other[ItemsOtherId::SEEDS].each { |seed|
next if not seed.flags.in_building
next if not seed.itemrefs.find { |ref| ref._rtti_classname == 'general_ref_building_holderst' }
next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index]
cnt[seed.mat_index] += 1
suspend {
world.items.other[ItemsOtherId::SEEDS].each { |seed|
next if not seed.flags.in_building
next if not seed.itemrefs.find { |ref| ref._rtti_classname == 'general_ref_building_holderst' }
next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index]
cnt[seed.mat_index] += 1
}
}
cnt.sort_by { |mat, c| c }.each { |mat, c|
name = world.raws.plants.all[mat].id
@ -126,13 +136,15 @@ def self.growcrops(material=nil, count_max=100)
end
cnt = 0
world.items.other[ItemsOtherId::SEEDS].each { |seed|
next if wantmat and seed.mat_index != wantmat
next if not seed.flags.in_building
next if not seed.itemrefs.find { |ref| ref._rtti_classname == 'general_ref_building_holderst' }
next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index]
seed.grow_counter = @raws_plant_growdur[seed.mat_index]
cnt += 1
suspend {
world.items.other[ItemsOtherId::SEEDS].each { |seed|
next if wantmat and seed.mat_index != wantmat
next if not seed.flags.in_building
next if not seed.itemrefs.find { |ref| ref._rtti_classname == 'general_ref_building_holderst' }
next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index]
seed.grow_counter = @raws_plant_growdur[seed.mat_index]
cnt += 1
}
}
puts "Grown #{cnt} crops"
end

@ -30,7 +30,6 @@ enum RB_command {
RB_IDLE,
RB_INIT,
RB_DIE,
RB_LOAD,
RB_EVAL,
RB_CUSTOM,
};
@ -70,10 +69,6 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <Plug
"Ruby interpreter. Eval() a ruby string.",
df_rubyeval));
commands.push_back(PluginCommand("r",
"Ruby interpreter dev. Eval() a ruby string.",
df_rubyeval));
return CR_OK;
}
@ -98,24 +93,26 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
return CR_OK;
}
DFhackCExport command_result plugin_onupdate ( color_ostream &out )
// send a single ruby line to be evaluated by the ruby thread
static command_result plugin_eval_rb(const char *command)
{
if (!onupdate_active)
return CR_OK;
command_result ret;
// serialize 'accesses' to the ruby thread
m_mutex->lock();
if (!r_thread)
// raced with plugin_shutdown ?
return CR_OK;
r_type = RB_EVAL;
r_command = "DFHack.onupdate";
r_command = command;
m_irun->unlock();
// could use a condition_variable or something...
while (r_type != RB_IDLE)
tthread::this_thread::yield();
// XXX non-atomic with previous r_type change check
ret = r_result;
m_irun->lock();
@ -124,36 +121,49 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
return ret;
}
static command_result df_rubyload(color_ostream &out, std::vector <std::string> & parameters)
static command_result plugin_eval_rb(std::string &command)
{
command_result ret;
return plugin_eval_rb(command.c_str());
}
DFhackCExport command_result plugin_onupdate ( color_ostream &out )
{
if (!onupdate_active)
return CR_OK;
return plugin_eval_rb("DFHack.onupdate");
}
DFhackCExport command_result plugin_onstatechange ( color_ostream &out, state_change_event e)
{
std::string cmd = "DFHack.onstatechange ";
switch (e) {
#define SCASE(s) case SC_ ## s : cmd += ":" # s ; break
SCASE(WORLD_LOADED);
SCASE(WORLD_UNLOADED);
SCASE(MAP_LOADED);
SCASE(MAP_UNLOADED);
SCASE(VIEWSCREEN_CHANGED);
SCASE(CORE_INITIALIZED);
SCASE(BEGIN_UNLOAD);
}
return plugin_eval_rb(cmd);
}
static command_result df_rubyload(color_ostream &out, std::vector <std::string> & parameters)
{
if (parameters.size() == 1 && (parameters[0] == "help" || parameters[0] == "?"))
{
out.print("This command loads the ruby script whose path is given as parameter, and run it.\n");
return CR_OK;
}
// serialize 'accesses' to the ruby thread
m_mutex->lock();
if (!r_thread)
// raced with plugin_shutdown ?
return CR_OK;
r_type = RB_LOAD;
r_command = parameters[0].c_str();
m_irun->unlock();
std::string cmd = "load '";
cmd += parameters[0]; // TODO escape singlequotes
cmd += "'";
// could use a condition_variable or something...
while (r_type != RB_IDLE)
tthread::this_thread::yield();
// XXX non-atomic with previous r_type change check
ret = r_result;
m_irun->lock();
m_mutex->unlock();
return ret;
return plugin_eval_rb(cmd);
}
static command_result df_rubyeval(color_ostream &out, std::vector <std::string> & parameters)
@ -167,32 +177,14 @@ static command_result df_rubyeval(color_ostream &out, std::vector <std::string>
}
std::string full = "";
full += "DFHack.puts((";
for (unsigned i=0 ; i<parameters.size() ; ++i) {
full += parameters[i];
full += " ";
if (i != parameters.size()-1)
full += " ";
}
full += ").inspect)";
m_mutex->lock();
if (!r_thread)
return CR_OK;
r_type = RB_EVAL;
r_command = full.c_str();
m_irun->unlock();
while (r_type != RB_IDLE)
tthread::this_thread::yield();
ret = r_result;
m_irun->lock();
m_mutex->unlock();
return ret;
return plugin_eval_rb(full);
}
@ -219,6 +211,8 @@ static void dump_rb_error(void)
Core::printerr(" %s\n", rb_string_value_ptr(&s));
}
static color_ostream_proxy *console_proxy;
// ruby thread main loop
static void df_rubythread(void *p)
{
@ -233,6 +227,8 @@ static void df_rubythread(void *p)
// create the ruby objects to map DFHack to ruby methods
ruby_bind_dfhack();
console_proxy = new color_ostream_proxy(Core::getInstance().getConsole());
r_result = CR_OK;
r_type = RB_IDLE;
@ -251,13 +247,6 @@ static void df_rubythread(void *p)
ruby_finalize();
break;
case RB_LOAD:
state = 0;
rb_load_protect(rb_str_new2(r_command), Qfalse, &state);
if (state)
dump_rb_error();
break;
case RB_EVAL:
state = 0;
rb_eval_string_protect(r_command, &state);
@ -339,9 +328,7 @@ static VALUE rb_dfrebase_delta(void)
static VALUE rb_dfprint_str(VALUE self, VALUE s)
{
// TODO color_ostream proxy yadda yadda
//getcore().con.print("%s", rb_string_value_ptr(&s));
Core::printerr("%s", rb_string_value_ptr(&s));
console_proxy->print("%s", rb_string_value_ptr(&s));
return Qnil;
}
@ -622,7 +609,7 @@ static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr, VALUE idx)
static VALUE rb_dfmemory_bitarray_length(VALUE self, VALUE addr)
{
DFHack::BitArray<int> *b = (DFHack::BitArray<int>*)rb_num2ulong(addr);
return rb_uint2inum(b->size*8); // b->size is in bytes
return rb_uint2inum(b->size*8); // b->size is in bytes
}
static VALUE rb_dfmemory_bitarray_resize(VALUE self, VALUE addr, VALUE sz)
{

@ -55,9 +55,30 @@ module DFHack
# this method is called by dfhack every 'onupdate' if onupdate_active is true
def onupdate
@onupdate_list ||= []
@onupdate_list.each { |cb| cb.call }
end
# register a callback to be called every gframe or more
# ex: DFHack.onstatechange_register { |newstate| puts "state changed to #{newstate}" }
def onstatechange_register(&b)
@onstatechange_list ||= []
@onstatechange_list << b
@onstatechange_list.last
end
# delete the callback for onstatechange ; use the value returned by onstatechange_register
def onstatechange_unregister(b)
@onstatechange_list.delete b
end
# this method is called by dfhack every 'onstatechange'
def onstatechange(newstate)
@onstatechange_list ||= []
@onstatechange_list.each { |cb| cb.call(newstate) }
end
# return an Unit
# with no arg, return currently selected unit in df UI ('v' or 'k' menu)
# with numeric arg, search unit by unit.id
@ -237,26 +258,6 @@ module DFHack
may = rawlist.find_all { |r| r.downcase.index(name.downcase) }
may.first if may.length == 1
end
def test
puts "starting"
suspend {
puts "cursor pos: #{cursor.x} #{cursor.y} #{cursor.z}"
if u = find_unit
puts "selected unit id: #{u.id}"
end
if b = map_block_at(cursor)
map_designation_at(cursor).dig = TileDigDesignation::Default
b.flags.designated = true
puts "dug cursor tile"
end
}
puts "done"
end
end
end