ruby: fix onupdate tick limiting + advmode, add pageprotect, add :script_finished

develop
jj 2012-12-11 17:25:51 +01:00
parent 6bc791d985
commit 662d3101c7
5 changed files with 108 additions and 17 deletions

@ -316,7 +316,7 @@ static command_result runRubyScript(color_ostream &out, PluginManager *plug_mgr,
rbcmd += "'" + args[i] + "', "; rbcmd += "'" + args[i] + "', ";
rbcmd += "]\n"; rbcmd += "]\n";
rbcmd += "load './hack/scripts/" + name + ".rb'"; rbcmd += "catch(:script_finished) { load './hack/scripts/" + name + ".rb' }";
return plug_mgr->eval_ruby(out, rbcmd.c_str()); return plug_mgr->eval_ruby(out, rbcmd.c_str());
} }

@ -48,6 +48,7 @@ If you create such a script, e.g. 'test.rb', that will add a new dfhack console
command 'test'. command 'test'.
The script can access the console command arguments through the global variable The script can access the console command arguments through the global variable
'$script_args', which is an array of ruby Strings. '$script_args', which is an array of ruby Strings.
To exit early from a script, use 'throw :script_finished'
The help string displayed in dfhack 'ls' command is the first line of the The help string displayed in dfhack 'ls' command is the first line of the
script, if it is a comment (ie starts with '# '). script, if it is a comment (ie starts with '# ').

@ -39,7 +39,7 @@ static color_ostream *r_console; // color_ostream given as argument, if NU
static const char *r_command; static const char *r_command;
static tthread::thread *r_thread; static tthread::thread *r_thread;
static int onupdate_active; static int onupdate_active;
static int onupdate_minyear, onupdate_minyeartick; static int onupdate_minyear, onupdate_minyeartick=-1, onupdate_minyeartickadv=-1;
static color_ostream_proxy *console_proxy; static color_ostream_proxy *console_proxy;
@ -180,10 +180,15 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
if (!onupdate_active) if (!onupdate_active)
return CR_OK; return CR_OK;
if (*df::global::cur_year < onupdate_minyear) if (df::global::cur_year && (*df::global::cur_year < onupdate_minyear))
return CR_OK; return CR_OK;
if (*df::global::cur_year == onupdate_minyear && if (df::global::cur_year_tick && onupdate_minyeartick >= 0 &&
*df::global::cur_year_tick < onupdate_minyeartick) (*df::global::cur_year == onupdate_minyear &&
*df::global::cur_year_tick < onupdate_minyeartick))
return CR_OK;
if (df::global::cur_year_tick_advmode && onupdate_minyeartickadv >= 0 &&
(*df::global::cur_year == onupdate_minyear &&
*df::global::cur_year_tick_advmode < onupdate_minyeartickadv))
return CR_OK; return CR_OK;
return plugin_eval_ruby(out, "DFHack.onupdate"); return plugin_eval_ruby(out, "DFHack.onupdate");
@ -481,6 +486,17 @@ static VALUE rb_dfonupdate_minyeartick_set(VALUE self, VALUE val)
return Qtrue; return Qtrue;
} }
static VALUE rb_dfonupdate_minyeartickadv(VALUE self)
{
return rb_uint2inum(onupdate_minyeartickadv);
}
static VALUE rb_dfonupdate_minyeartickadv_set(VALUE self, VALUE val)
{
onupdate_minyeartickadv = rb_num2ulong(val);
return Qtrue;
}
static VALUE rb_dfprint_str(VALUE self, VALUE s) static VALUE rb_dfprint_str(VALUE self, VALUE s)
{ {
if (r_console) if (r_console)
@ -531,6 +547,23 @@ static VALUE rb_dfget_vtable_ptr(VALUE self, VALUE objptr)
return rb_uint2inum(*(uint32_t*)rb_num2ulong(objptr)); return rb_uint2inum(*(uint32_t*)rb_num2ulong(objptr));
} }
// run a dfhack command, as if typed from the dfhack console
static VALUE rb_dfhack_run(VALUE self, VALUE cmd)
{
if (!r_console) // XXX
return Qnil;
std::string s;
int strlen = FIX2INT(rb_funcall(cmd, rb_intern("length"), 0));
s.assign(rb_string_value_ptr(&cmd), strlen);
// allow the target command to suspend
// FIXME
//CoreSuspendClaimer suspend(true);
Core::getInstance().runCommand(*r_console, s);
return Qtrue;
}
@ -663,6 +696,49 @@ static VALUE rb_dfmemory_patch(VALUE self, VALUE addr, VALUE raw)
return ret ? Qtrue : Qfalse; return ret ? Qtrue : Qfalse;
} }
// allocate memory pages
static VALUE rb_dfmemory_pagealloc(VALUE self, VALUE len)
{
void *ret = Core::getInstance().p->memAlloc(rb_num2ulong(len));
return (ret == (void*)-1) ? Qnil : rb_uint2inum((uint32_t)ret);
}
// free memory from pagealloc
static VALUE rb_dfmemory_pagedealloc(VALUE self, VALUE ptr, VALUE len)
{
int ret = Core::getInstance().p->memDealloc((void*)rb_num2ulong(ptr), rb_num2ulong(len));
return ret ? Qfalse : Qtrue;
}
// change memory page permissions
// ptr must be page-aligned
// prot is a String, eg 'rwx', 'r', 'x'
static VALUE rb_dfmemory_pageprotect(VALUE self, VALUE ptr, VALUE len, VALUE prot_str)
{
int ret, prot=0;
char *prot_p = rb_string_value_ptr(&prot_str);
if (*prot_p == 'r') {
prot |= Process::MemProt::READ;
++prot_p;
}
if (*prot_p == 'w') {
prot |= Process::MemProt::WRITE;
++prot_p;
}
if (*prot_p == 'x') {
prot |= Process::MemProt::EXEC;
++prot_p;
}
Core::printerr("pageprot %x %x %x\n", rb_num2ulong(ptr), rb_num2ulong(len), prot);
ret = Core::getInstance().p->memProtect((void*)rb_num2ulong(ptr), rb_num2ulong(len), prot);
return ret ? Qfalse : Qtrue;
}
// stl::string // stl::string
static VALUE rb_dfmemory_stlstring_new(VALUE self) static VALUE rb_dfmemory_stlstring_new(VALUE self)
@ -963,14 +1039,20 @@ static void ruby_bind_dfhack(void) {
rb_define_singleton_method(rb_cDFHack, "onupdate_minyear=", RUBY_METHOD_FUNC(rb_dfonupdate_minyear_set), 1); rb_define_singleton_method(rb_cDFHack, "onupdate_minyear=", RUBY_METHOD_FUNC(rb_dfonupdate_minyear_set), 1);
rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartick", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartick), 0); rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartick", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartick), 0);
rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartick=", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartick_set), 1); rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartick=", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartick_set), 1);
rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartickadv", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartickadv), 0);
rb_define_singleton_method(rb_cDFHack, "onupdate_minyeartickadv=", RUBY_METHOD_FUNC(rb_dfonupdate_minyeartickadv_set), 1);
rb_define_singleton_method(rb_cDFHack, "get_global_address", RUBY_METHOD_FUNC(rb_dfget_global_address), 1); rb_define_singleton_method(rb_cDFHack, "get_global_address", RUBY_METHOD_FUNC(rb_dfget_global_address), 1);
rb_define_singleton_method(rb_cDFHack, "get_vtable", RUBY_METHOD_FUNC(rb_dfget_vtable), 1); rb_define_singleton_method(rb_cDFHack, "get_vtable", RUBY_METHOD_FUNC(rb_dfget_vtable), 1);
rb_define_singleton_method(rb_cDFHack, "get_rtti_classname", RUBY_METHOD_FUNC(rb_dfget_rtti_classname), 1); rb_define_singleton_method(rb_cDFHack, "get_rtti_classname", RUBY_METHOD_FUNC(rb_dfget_rtti_classname), 1);
rb_define_singleton_method(rb_cDFHack, "get_vtable_ptr", RUBY_METHOD_FUNC(rb_dfget_vtable_ptr), 1); rb_define_singleton_method(rb_cDFHack, "get_vtable_ptr", RUBY_METHOD_FUNC(rb_dfget_vtable_ptr), 1);
//rb_define_singleton_method(rb_cDFHack, "dfhack_run", RUBY_METHOD_FUNC(rb_dfhack_run), 1);
rb_define_singleton_method(rb_cDFHack, "print_str", RUBY_METHOD_FUNC(rb_dfprint_str), 1); rb_define_singleton_method(rb_cDFHack, "print_str", RUBY_METHOD_FUNC(rb_dfprint_str), 1);
rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1); rb_define_singleton_method(rb_cDFHack, "print_err", RUBY_METHOD_FUNC(rb_dfprint_err), 1);
rb_define_singleton_method(rb_cDFHack, "malloc", RUBY_METHOD_FUNC(rb_dfmalloc), 1); rb_define_singleton_method(rb_cDFHack, "malloc", RUBY_METHOD_FUNC(rb_dfmalloc), 1);
rb_define_singleton_method(rb_cDFHack, "free", RUBY_METHOD_FUNC(rb_dffree), 1); rb_define_singleton_method(rb_cDFHack, "free", RUBY_METHOD_FUNC(rb_dffree), 1);
rb_define_singleton_method(rb_cDFHack, "pagealloc", RUBY_METHOD_FUNC(rb_dfmemory_pagealloc), 1);
rb_define_singleton_method(rb_cDFHack, "pagedealloc", RUBY_METHOD_FUNC(rb_dfmemory_pagedealloc), 2);
rb_define_singleton_method(rb_cDFHack, "pageprotect", RUBY_METHOD_FUNC(rb_dfmemory_pageprotect), 3);
rb_define_singleton_method(rb_cDFHack, "vmethod_do_call", RUBY_METHOD_FUNC(rb_dfvcall), 8); rb_define_singleton_method(rb_cDFHack, "vmethod_do_call", RUBY_METHOD_FUNC(rb_dfvcall), 8);
rb_define_singleton_method(rb_cDFHack, "version", RUBY_METHOD_FUNC(rb_dfversion), 0); rb_define_singleton_method(rb_cDFHack, "version", RUBY_METHOD_FUNC(rb_dfversion), 0);

@ -64,6 +64,7 @@ module DFHack
# register a callback to be called every gframe or more # register a callback to be called every gframe or more
# ex: DFHack.onupdate_register('fastdwarf') { DFHack.world.units[0].counters.job_counter = 0 } # ex: DFHack.onupdate_register('fastdwarf') { DFHack.world.units[0].counters.job_counter = 0 }
# if ticklimit is given, do not call unless this much game ticks have passed. Handles advmode time stretching.
def onupdate_register(descr, ticklimit=nil, initialtickdelay=0, &b) def onupdate_register(descr, ticklimit=nil, initialtickdelay=0, &b)
raise ArgumentError, 'need a description as 1st arg' unless descr.kind_of?(::String) raise ArgumentError, 'need a description as 1st arg' unless descr.kind_of?(::String)
@onupdate_list ||= [] @onupdate_list ||= []
@ -82,7 +83,7 @@ module DFHack
@onupdate_list.delete b @onupdate_list.delete b
if @onupdate_list.empty? if @onupdate_list.empty?
DFHack.onupdate_active = false DFHack.onupdate_active = false
DFHack.onupdate_minyear = DFHack.onupdate_minyeartick = 0 DFHack.onupdate_minyear = DFHack.onupdate_minyeartick = DFHack.onupdate_minyeartickadv = -1
end end
end end
@ -94,20 +95,32 @@ module DFHack
end end
TICKS_PER_YEAR = 1200*28*12 TICKS_PER_YEAR = 1200*28*12
# this method is called by dfhack every 'onupdate' if onupdate_active is true # this method is called by ruby.cpp if df.onupdate_active is true
def onupdate def onupdate
@onupdate_list ||= [] @onupdate_list ||= []
ticks_per_year = TICKS_PER_YEAR y = cur_year
ticks_per_year *= 72 if gametype == :ADVENTURE_MAIN or gametype == :ADVENTURE_ARENA ytmax = TICKS_PER_YEAR
if df.gamemode == :ADVENTURE and df.respond_to?(:cur_year_tick_advmode)
yt = cur_year_tick_advmode
ytmax *= 144
else
yt = cur_year_tick
end
@onupdate_list.each { |o| @onupdate_list.each { |o|
o.check_run(cur_year, cur_year_tick, ticks_per_year) o.check_run(y, yt, ytmax)
} }
if onext = @onupdate_list.sort.first if onext = @onupdate_list.sort.first
DFHack.onupdate_minyear = onext.minyear DFHack.onupdate_minyear = onext.minyear
if ytmax > TICKS_PER_YEAR
DFHack.onupdate_minyeartick = -1
DFHack.onupdate_minyeartickadv = onext.minyeartick
else
DFHack.onupdate_minyeartick = onext.minyeartick DFHack.onupdate_minyeartick = onext.minyeartick
DFHack.onupdate_minyeartickadv = -1
end
end end
end end

@ -8,12 +8,7 @@ when 'add'
if u = df.unit_find if u = df.unit_find
$superdwarf_ids |= [u.id] $superdwarf_ids |= [u.id]
if df.gamemode == :ADVENTURE and not df.respond_to?(:cur_year_tick_advmode) $superdwarf_onupdate ||= df.onupdate_register('superdwarf', 1) {
onupdate_delay = nil
else
onupdate_delay = 1
end
$superdwarf_onupdate ||= df.onupdate_register('superdwarf', onupdate_delay) {
if $superdwarf_ids.empty? if $superdwarf_ids.empty?
df.onupdate_unregister($superdwarf_onupdate) df.onupdate_unregister($superdwarf_onupdate)
$superdwarf_onupdate = nil $superdwarf_onupdate = nil