ruby: tweak onupdate to allow tick rate-limiting

develop
jj 2012-07-05 15:35:37 +02:00
parent f560d2de11
commit 7732901b6e
3 changed files with 98 additions and 11 deletions

@ -124,6 +124,10 @@ DFHack callbacks
The plugin interfaces with dfhack 'onupdate' hook.
To register ruby code to be run every graphic frame, use:
handle = df.onupdate_register { puts 'i love flooding the console' }
You can also rate-limit when your callback is called to a number of game ticks:
handle = df.onupdate_register(10) { puts '10 more in-game ticks elapsed' }
In this case, the callback is called immediately, and then every X in-game
ticks (advances only when the game is unpaused).
To stop being called, use:
df.onupdate_unregister handle

@ -6,8 +6,7 @@
#include "VersionInfo.h"
#include "DataDefs.h"
#include "df/world.h"
#include "df/unit.h"
#include "df/global_objects.h"
#include "tinythread.h"
@ -39,6 +38,7 @@ static color_ostream *r_console; // color_ostream given as argument, if NU
static const char *r_command;
static tthread::thread *r_thread;
static int onupdate_active;
static int onupdate_minyear, onupdate_minyeartick;
static color_ostream_proxy *console_proxy;
@ -165,10 +165,15 @@ DFhackCExport command_result plugin_onupdate ( color_ostream &out )
// ruby sets this flag when needed, to avoid lag running ruby code every
// frame if not necessary
// TODO dynamic check on df::cur_year{_tick}
if (!onupdate_active)
return CR_OK;
if (*df::global::cur_year < onupdate_minyear)
return CR_OK;
if (*df::global::cur_year == onupdate_minyear &&
*df::global::cur_year_tick < onupdate_minyeartick)
return CR_OK;
return plugin_eval_ruby(out, "DFHack.onupdate");
}
@ -422,7 +427,7 @@ static VALUE rb_cDFHack;
// DFHack module ruby methods, binds specific dfhack methods
// enable/disable calls to DFHack.onupdate()
static VALUE rb_dfonupdateactive(VALUE self)
static VALUE rb_dfonupdate_active(VALUE self)
{
if (onupdate_active)
return Qtrue;
@ -430,12 +435,34 @@ static VALUE rb_dfonupdateactive(VALUE self)
return Qfalse;
}
static VALUE rb_dfonupdateactiveset(VALUE self, VALUE val)
static VALUE rb_dfonupdate_active_set(VALUE self, VALUE val)
{
onupdate_active = (BOOL_ISFALSE(val) ? 0 : 1);
return Qtrue;
}
static VALUE rb_dfonupdate_minyear(VALUE self)
{
return rb_uint2inum(onupdate_minyear);
}
static VALUE rb_dfonupdate_minyear_set(VALUE self, VALUE val)
{
onupdate_minyear = rb_num2ulong(val);
return Qtrue;
}
static VALUE rb_dfonupdate_minyeartick(VALUE self)
{
return rb_uint2inum(onupdate_minyeartick);
}
static VALUE rb_dfonupdate_minyeartick_set(VALUE self, VALUE val)
{
onupdate_minyeartick = rb_num2ulong(val);
return Qtrue;
}
static VALUE rb_dfprint_str(VALUE self, VALUE s)
{
if (r_console)
@ -777,8 +804,12 @@ static VALUE rb_dfvcall(VALUE self, VALUE cppobj, VALUE cppvoff, VALUE a0, VALUE
static void ruby_bind_dfhack(void) {
rb_cDFHack = rb_define_module("DFHack");
rb_define_singleton_method(rb_cDFHack, "onupdate_active", RUBY_METHOD_FUNC(rb_dfonupdateactive), 0);
rb_define_singleton_method(rb_cDFHack, "onupdate_active=", RUBY_METHOD_FUNC(rb_dfonupdateactiveset), 1);
rb_define_singleton_method(rb_cDFHack, "onupdate_active", RUBY_METHOD_FUNC(rb_dfonupdate_active), 0);
rb_define_singleton_method(rb_cDFHack, "onupdate_active=", RUBY_METHOD_FUNC(rb_dfonupdate_active_set), 1);
rb_define_singleton_method(rb_cDFHack, "onupdate_minyear", RUBY_METHOD_FUNC(rb_dfonupdate_minyear), 0);
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_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_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);

@ -23,26 +23,78 @@ module Kernel
end
module DFHack
class OnupdateCallback
attr_accessor :callback, :timelimit, :minyear, :minyeartick
def initialize(cb, tl)
@callback = cb
@ticklimit = tl
@minyear = (tl ? df.cur_year : 0)
@minyeartick = (tl ? df.cur_year_tick : 0)
end
# run callback if timedout
def check_run(year, yeartick, yearlen)
if !@ticklimit
@callback.call
else
if year > @minyear or (year == @minyear and yeartick >= @minyeartick)
@callback.call
@minyear = year
@minyeartick = yeartick + @ticklimit
if @minyeartick > yearlen
@minyear += 1
@minyeartick -= yearlen
end
end
end
end
def <=>(o)
[@minyear, @minyeartick] <=> [o.minyear, o.minyeartick]
end
end
class << self
attr_accessor :onupdate_list, :onstatechange_list
# register a callback to be called every gframe or more
# ex: DFHack.onupdate_register { DFHack.world.units[0].counters.job_counter = 0 }
def onupdate_register(&b)
def onupdate_register(ticklimit=nil, &b)
@onupdate_list ||= []
@onupdate_list << b
@onupdate_list << OnupdateCallback.new(b, ticklimit)
DFHack.onupdate_active = true
if onext = @onupdate_list.sort.first
DFHack.onupdate_minyear = onext.minyear
DFHack.onupdate_minyeartick = onext.minyeartick
end
@onupdate_list.last
end
# delete the callback for onupdate ; use the value returned by onupdate_register
def onupdate_unregister(b)
@onupdate_list.delete b
DFHack.onupdate_active = false if @onupdate_list.empty?
if @onupdate_list.empty?
DFHack.onupdate_active = false
DFHack.onupdate_minyear = DFHack.onupdate_minyeartick = 0
end
end
TICKS_PER_YEAR = 1200*28*12
# this method is called by dfhack every 'onupdate' if onupdate_active is true
def onupdate
@onupdate_list ||= []
@onupdate_list.each { |cb| cb.call }
ticks_per_year = TICKS_PER_YEAR
ticks_per_year *= 72 if gametype == :ADVENTURE_MAIN or gametype == :ADVENTURE_ARENA
@onupdate_list.each { |o|
o.check_run(cur_year, cur_year_tick, ticks_per_year)
}
if onext = @onupdate_list.sort.first
DFHack.onupdate_minyear = onext.minyear
DFHack.onupdate_minyeartick = onext.minyeartick
end
end
# register a callback to be called every gframe or more