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) add_subdirectory (dwarfexport)
endif() endif()
OPTION(BUILD_RUBY "Build ruby binding." OFF) OPTION(BUILD_RUBY "Build ruby binding." ON)
if (BUILD_RUBY) if (BUILD_RUBY)
add_subdirectory (ruby) add_subdirectory (ruby)
endif() endif()

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

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

@ -30,7 +30,6 @@ enum RB_command {
RB_IDLE, RB_IDLE,
RB_INIT, RB_INIT,
RB_DIE, RB_DIE,
RB_LOAD,
RB_EVAL, RB_EVAL,
RB_CUSTOM, RB_CUSTOM,
}; };
@ -70,10 +69,6 @@ DFhackCExport command_result plugin_init ( color_ostream &out, std::vector <Plug
"Ruby interpreter. Eval() a ruby string.", "Ruby interpreter. Eval() a ruby string.",
df_rubyeval)); df_rubyeval));
commands.push_back(PluginCommand("r",
"Ruby interpreter dev. Eval() a ruby string.",
df_rubyeval));
return CR_OK; return CR_OK;
} }
@ -98,24 +93,26 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out )
return CR_OK; 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; command_result ret;
// serialize 'accesses' to the ruby thread
m_mutex->lock(); m_mutex->lock();
if (!r_thread) if (!r_thread)
// raced with plugin_shutdown ?
return CR_OK; return CR_OK;
r_type = RB_EVAL; r_type = RB_EVAL;
r_command = "DFHack.onupdate"; r_command = command;
m_irun->unlock(); m_irun->unlock();
// could use a condition_variable or something...
while (r_type != RB_IDLE) while (r_type != RB_IDLE)
tthread::this_thread::yield(); tthread::this_thread::yield();
// XXX non-atomic with previous r_type change check
ret = r_result; ret = r_result;
m_irun->lock(); m_irun->lock();
@ -124,36 +121,49 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
return ret; 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] == "?")) 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"); out.print("This command loads the ruby script whose path is given as parameter, and run it.\n");
return CR_OK; return CR_OK;
} }
// serialize 'accesses' to the ruby thread std::string cmd = "load '";
m_mutex->lock(); cmd += parameters[0]; // TODO escape singlequotes
if (!r_thread) cmd += "'";
// raced with plugin_shutdown ?
return CR_OK;
r_type = RB_LOAD;
r_command = parameters[0].c_str();
m_irun->unlock();
// could use a condition_variable or something... return plugin_eval_rb(cmd);
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;
} }
static command_result df_rubyeval(color_ostream &out, std::vector <std::string> & parameters) 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 = ""; std::string full = "";
full += "DFHack.puts((";
for (unsigned i=0 ; i<parameters.size() ; ++i) { for (unsigned i=0 ; i<parameters.size() ; ++i) {
full += parameters[i]; full += parameters[i];
full += " "; if (i != parameters.size()-1)
full += " ";
} }
full += ").inspect)"; return plugin_eval_rb(full);
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;
} }
@ -219,6 +211,8 @@ static void dump_rb_error(void)
Core::printerr(" %s\n", rb_string_value_ptr(&s)); Core::printerr(" %s\n", rb_string_value_ptr(&s));
} }
static color_ostream_proxy *console_proxy;
// ruby thread main loop // ruby thread main loop
static void df_rubythread(void *p) 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 // create the ruby objects to map DFHack to ruby methods
ruby_bind_dfhack(); ruby_bind_dfhack();
console_proxy = new color_ostream_proxy(Core::getInstance().getConsole());
r_result = CR_OK; r_result = CR_OK;
r_type = RB_IDLE; r_type = RB_IDLE;
@ -251,13 +247,6 @@ static void df_rubythread(void *p)
ruby_finalize(); ruby_finalize();
break; 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: case RB_EVAL:
state = 0; state = 0;
rb_eval_string_protect(r_command, &state); 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) static VALUE rb_dfprint_str(VALUE self, VALUE s)
{ {
// TODO color_ostream proxy yadda yadda console_proxy->print("%s", rb_string_value_ptr(&s));
//getcore().con.print("%s", rb_string_value_ptr(&s));
Core::printerr("%s", rb_string_value_ptr(&s));
return Qnil; 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) static VALUE rb_dfmemory_bitarray_length(VALUE self, VALUE addr)
{ {
DFHack::BitArray<int> *b = (DFHack::BitArray<int>*)rb_num2ulong(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) 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 # this method is called by dfhack every 'onupdate' if onupdate_active is true
def onupdate def onupdate
@onupdate_list ||= []
@onupdate_list.each { |cb| cb.call } @onupdate_list.each { |cb| cb.call }
end 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 # return an Unit
# with no arg, return currently selected unit in df UI ('v' or 'k' menu) # with no arg, return currently selected unit in df UI ('v' or 'k' menu)
# with numeric arg, search unit by unit.id # 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 = rawlist.find_all { |r| r.downcase.index(name.downcase) }
may.first if may.length == 1 may.first if may.length == 1
end 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
end end