Zeriab 9 Report post Posted November 27, 2008 Scheduler ~ Scripting Tool Version: 1.0 Introduction How many of you tried creating a parallel process where you put in an amount of frames and then a script call or something else afterwards? Basically a simply way of doing something after an amount of frames. This is the principle behind the script. To schedule pieces of code to be executed after an arbitrary amount of frames. No making decrementing counters. The scheduler takes care of all that or well most of it. It is still not as simple as using the wait event command. Script #============================================================================== # ** Scheduler #------------------------------------------------------------------------------ # This class allows to schedule a proc or method call a given amount of frames # into the future with any amount of arguments #============================================================================== class Scheduler #========================================================================== # ** Order #---------------------------------------------------------------------------- # An order is a proc, method or something else which has 'call' as a method, # and the arguments to pass along. #========================================================================== # Create an struct for containing the data Order = Struct.new(:callable, :arguments) # Extend the class with a call-method which calls the callable with the args class Order #------------------------------------------------------------------------ # * Call the callable with the present arguments #------------------------------------------------------------------------ def call callable.call(*arguments) end end #========================================================================== # ** RecurringOrder #---------------------------------------------------------------------------- # An order which is recurring every specified amount of time until # FalseClass is returned from the call. # Note that arguments remain the same for each call #========================================================================== # Create an struct for containing the data RecurringOrder = Struct.new(:callable, :arguments, :frames) # Extend the class with a call-method which calls the callable with the args class RecurringOrder #------------------------------------------------------------------------ # * Call the callable with the present arguments #------------------------------------------------------------------------ def call result = callable.call(*arguments) unless result == FalseClass Scheduler.schedule_recurring(frames, frames, callable, *arguments) end end end #========================================================================== # ** Mapping #---------------------------------------------------------------------------- # Maps an index to an array. Values can be added to these value. # Each array starts empty. #========================================================================== class Mapping #------------------------------------------------------------------------ # * Initialization #------------------------------------------------------------------------ def initialize @mapping = {} end #------------------------------------------------------------------------ # * Add an value to a given index #------------------------------------------------------------------------ def add(index, value) @mapping[index] = [] if @mapping[index].nil? @mapping[index] << value end #------------------------------------------------------------------------ # * Retrieve the list of values mapped to the index #------------------------------------------------------------------------ def get(index) return [] if @mapping[index].nil? @mapping[index] end #------------------------------------------------------------------------ # * Delete the array the index is mapped to. Conceptually it is now empty #------------------------------------------------------------------------ def empty(index) @mapping.delete(index) end end #-------------------------------------------------------------------------- # * Initialization #-------------------------------------------------------------------------- def initialize # This maps @mapping = Mapping.new @tick = 0 end #-------------------------------------------------------------------------- # * Scheduling #-------------------------------------------------------------------------- def schedule(frames, callable, *arguments) # Create an order order = Order.new(callable, arguments) @mapping.add(frames + @tick, order) end #-------------------------------------------------------------------------- # * Scheduling #-------------------------------------------------------------------------- def schedule_recurring(frames, frames_to_wait, callable, *arguments) # Create an order order = RecurringOrder.new(callable, arguments, frames_to_wait) @mapping.add(frames + @tick, order) end #-------------------------------------------------------------------------- # * Update the scheduler #-------------------------------------------------------------------------- def update # Get the orders for the current tick orders = @mapping.get(@tick) # Delete the mapping's reference to the list of orders @mapping.empty(@tick) # Call each order for order in orders order.call end # Advance the tick (next frame) @tick += 1 end #-------------------------------------------------------------------------- # * 'Singleton' principle used although you can easily make # an extra scheduler. (Class method only works for this) #-------------------------------------------------------------------------- @@instance = self.new def self.instance return @@instance end ## Class methods point to the equivalent instance methods def self.schedule_recurring(*args) instance.schedule_recurring(*args); end def self.schedule(*args) instance.schedule(*args); end def self.update(*args) instance.update(*args); end end The latest version will be present here: http://zeriab.plesk3.freepgs.com/root/scri...r/scheduler.txt You could check it if more than a month has passed since last edit. You know, just in case. More generally all material related to the Scheduler except the forum topic can be found here: http://zeriab.plesk3.freepgs.com/index.php...ipts/Scheduler/ Here is a binding if you want the scheduler to work for every frame. Basically for each Graphic.update the scheduler is updated. It doesn't matter whether you are in a menu, title screen, battle. As long as Graphic.update is called so is the scheduler. (The main scheduler) module Graphics class << self unless self.method_defined?(:scheduler_update) alias :scheduler_update :update end def update(*args) scheduler_update(*args) Scheduler.update end end end Instructions <span onClick="this.nextSibling.style.display=((this.nextSibling.style.display=='none')?'':'none');" onclick="this.nextSibling.style.display='';"><div class='spoilertop'>Semi-big and evil instructions</div></span><span style='display:none;'><div class='spoilermain' >You can schedule in two ways. You can do a one-time schedule which works like this: (Using the class method) Scheduler.schedule(frames, callable, *arguments) # Here's an example Scheduler.schedule(65, Proc.new {|x,y| p x,y}, "A string", 42) The 65 means that the proc will be called after 65 frames. (Or 65 ticks to be more precise. 1 update = 1 tick usually) After 65 ticks the proc {|x,y| p x,y} will be called with the arguments x = "A string" and y = 42. (The *arguments means any number of arguments. This can also be no arguments at all) The Scheduler uses duck typing and assumes that anything which has the .call method works properly in the context. I imagine procs and methods to be the most commonly used. The next is that you can schedule a recurring call which works like this: (Using the class method) Scheduler.schedule_recurring(frames, frames_to_wait, callable, *arguments) # Here's an example Scheduler.schedule_recurring(65, 30, Proc.new {|x,y| p x,y}, "A string", 42) The arguments is the same as for the one-time with the addition of the frames_to_wait argument. This specifies that after the first 65 ticks each recurring call will happen after 30 ticks. This will continue until the callable returns FalseClass. (Mind you it's false.class and not false) Now you have to update the schedule every frame or it won't schedule properly. Here is a binding where the scheduler updates every time the Graphics module updates. (Made for XP. I am unsure whether this part works in VX) module Graphics class << self unless self.method_defined?(:scheduler_update) alias :scheduler_update :update end def update(*args) scheduler_update(*args) Scheduler.update end end end Note that the class methods only work for one scheduler. I believe this should be the generally working Scheduler.</div></span> Compatibility The Scheduler alone use only Ruby and could easily be placed in a Ruby if one wanted that. It is highly unlikely that you will encounter any compatibility issues with the backbone alone since it is independent from RGSS/2 library. On the other side there could potentially be problems with the bindings which makes use of the scheduler so it actually does something in game. Currently there is only the Graphics.update binding which makes compatibility issues very unlikely. Future Work - Explicitly exit/stop a scheduler. The scheduled items can then be discard, executed or maybe something else. - Error handling. (This may be an external. I.e. not embedded in the core) - Documentation Credits and Thanks Credits goes to Zeriab for creating the system I would like to thank everyone using their time to try and use my system. I would like to thank everyone reading this topic. Thanks. Author's Notes I would be delighted if you report any bug, errors or issues you find. In fact I would be delighted if you took the time and replied even if you have nothing to report. Suggestions are more than welcome. Note that I will release a demo which shows a couple of ways of using this script. Note also that I will always make a post when I have an update. (Small stuff like typos excluded) And finally: ENJOY! - Zeriab Share this post Link to post Share on other sites
Polraudio 122 Report post Posted November 28, 2008 Very nice script. Takes a bit to know what your doing but i like it. Share this post Link to post Share on other sites
Zeriab 9 Report post Posted November 30, 2008 I'm glad you like it ^^ There is indeed some learning curve, but once you know how to use it you can benefit greatly from it in some case. Share this post Link to post Share on other sites
Polraudio 122 Report post Posted November 30, 2008 I did learn to script better. Thanks Share this post Link to post Share on other sites
Zeriab 9 Report post Posted January 10, 2009 Here is an example of how you can use the script. It will auto-save the game to the first slot every 180 seconds or 3 minutes although the first time it will auto-save after 2 minutes. RMXP Version: class Game_Temp unless self.method_defined?(:scheduler) attr_writer :map_scheduler def map_scheduler @map_scheduler ||= Scheduler.new end end unless self.method_defined?(:zeriab_autosave_game_temp_initialize) alias zeriab_autosave_game_temp_initialize :initialize def initialize zeriab_autosave_game_temp_initialize map_scheduler.schedule_recurring(120 * Graphics.frame_rate, 180 * Graphics.frame_rate, Proc.new {s = Scene_Save.save(0)}) end end end class Scene_Map unless self.method_defined?(:zeriab_autosave_scene_map_update) alias zeriab_autosave_scene_map_update :update def update $game_temp.map_scheduler.update zeriab_autosave_scene_map_update end end end class Scene_Save def self.save(file_index) save_obj = self.new file = File.open(save_obj.make_filename(file_index), "wb") save_obj.write_save_data(file) file.close end end RMVX version: class Game_Temp unless self.method_defined?(:scheduler) attr_writer :map_scheduler def map_scheduler @map_scheduler ||= Scheduler.new end end unless self.method_defined?(:zeriab_autosave_game_temp_initialize) alias zeriab_autosave_game_temp_initialize :initialize def initialize zeriab_autosave_game_temp_initialize map_scheduler.schedule_recurring(120 * Graphics.frame_rate, 180 * Graphics.frame_rate, Proc.new {s = Scene_File.save(0)}) end end end class Scene_Map < Scene_Base unless self.method_defined?(:zeriab_autosave_scene_map_update) alias zeriab_autosave_scene_map_update :update def update $game_temp.map_scheduler.update zeriab_autosave_scene_map_update end end end class Scene_File < Scene_Base def self.save(file_index) save_obj = self.new(true, false, false) file = File.open(save_obj.make_filename(file_index), "wb") save_obj.write_save_data(file) file.close end end *hugs* - Zeriab Share this post Link to post Share on other sites
Polraudio 122 Report post Posted January 10, 2009 This is very nice an auto-save this is exactly what i was looking for, but im going to mod it where it saves after every scene. thank you Zeriab. Share this post Link to post Share on other sites
Zeriab 9 Report post Posted January 12, 2009 I am glad you like it ^^ I made it more for demonstrating an actual practical use of the scheduler rather than for making an auto-save script where I chose to use the Scheduler. Feel free to modify it in any way you want and just ask if you need help. *hugs* - Zeriab Share this post Link to post Share on other sites
Polraudio 122 Report post Posted January 12, 2009 Ok will do Share this post Link to post Share on other sites