Advertisement
Guest User

A debugger script by Mithran

a guest
Feb 14th, 2013
40
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 10.91 KB | None | 0 0
  1.     # Graphical Object Global Reference
  2.     # v 1.1
  3.     # A debugger script.
  4.     # Created by Mithran
  5.     # hosted at rpgmakervx.net; forums.rpgmakerweb.com
  6.      
  7.     # Created to address the issue of specific Game.exe crashes during play
  8.      
  9.     %q(
  10.     The cause of a given Game.exe crash could be any number of things - anything that
  11.     doesn't create throw an error in Ruby, but causes an unhandled exception in one
  12.    of the 'hidden' classes.
  13.    
  14.    After extensive testing, I was finally able to recreate the circumstances leading
  15.    up to one such exception that, if left unhandled, could lead to Game.exe crash.  
  16.    
  17.    1. A "GO" - Graphical Object (Sprite, Window, Plane, or Tilemap) is created
  18.    2. The Graphical Object is assigned a Viewport
  19.    3. The Viewport is disposed, but the sprite is not
  20.    4. The Graphical Object is claimed by GC (garbage disposal)*
  21.    
  22.    * - Newly Discovered: attempting to dispose a sprite that has a disposed viewport
  23.        will occasionally also crash. (v 1.1)
  24.        Note that this particuar crash only seems to occur if screen draws have taken
  25.        place (any of the Graphics methods) between the time the viewport is disposed
  26.        and the sprite disposal is attempted.
  27.    
  28.    Due to the way GC is implemented, you are unlikely to see an immediate effect
  29.    when the situation comes up.  It could be several scene changes down the line
  30.    before the crash finally happens.  To make matters worse, following the exact
  31.    same course of action will yield completely different results, making it seem
  32.    as though the crashes are random.  In addition, there is yet another circumstance
  33.    which I have still been unable to pinpoint, but I suspect has something to do
  34.    with the order in which assets associated with the Graphical Object are claimed
  35.    by the GC, or the amount of screen rewdraws that have taken place,
  36.    that allows the GO to be cliamed without causing an exception and
  37.    thus making it even harder to find.
  38.    
  39.    In essence: you could be suffering from an unstable game and not even know it.
  40.    
  41.    So that is where this little script comes in.  This does the following:
  42.    
  43.    1. Creates a global variable backreference to every Graphical Object created.
  44.      This prevents them from being marked by the GC so long as the reference exists,
  45.      circumventing the final condition to cause this version of the crash.
  46.    
  47.    2. Removes reference to the Graphical Object once it has been disposed.
  48.      This reallows the object to be marked by GC for disposal (once all other
  49.      references are removed).  Since the GO is disposed, condition 3 is no longer met
  50.      and the object is deemed 'safe'.
  51.    
  52.    3. Report on potential issues to the user.
  53.      This allows the user (given limited scripting knowledge) to identify potential
  54.      errors and fix them outright.
  55.    
  56.    4. Prevents further Game.exe crashes caused by this specific issue.
  57.      Includes a 'lazy' fix that cleans up offending Graphical Objects when the scene
  58.      changes.*
  59.    
  60.    * v 1.1 'Lazy' fix has been superceeded to prevent crashes caused by disposal of
  61.        these errant sprites.  Lazy fix only works if debug criticial disposal has
  62.        been disabled.
  63.      
  64.    Version History:
  65.    
  66.    v 1.1
  67.      Discovered a new condition for a Game.exe crash.  Updated script to trap and log
  68.      this error also.
  69.    
  70.    v 1.01-1.05
  71.      Minor bugfixes, improved logging, added consideration for Plane objects viewport method error
  72.    
  73.    v 1.0
  74.      Initial Release
  75.      
  76.    )
  77.    # Creates a global refrence list to all graphical objects, preventing them from
  78.    # ever being garbage collected.  Objects from this list are removed when the object
  79.    # runs its dispose method, thereby allowing them to be GC'd.  
  80.      
  81.     # Has a built in layer to notify the player if the scene changes with live
  82.     # graphical objects in play.  As a rule, this should almost never happen.  Certain
  83.     # scripts have sprites that are used across every scene and never disposed,
  84.     # thus intentionally having an additional global reference (such as mouse script)
  85.     # As such, they should never generate a critical error.  However, they can be manually
  86.     # exempted from being detected by this script by using the instance method
  87.     # 'gobj_exempt' on the sprite.  In the case of Woratana's Simple Mouse/Jets Mouse
  88.     # simply place my script as low as possible on the scripts list, but above Main,
  89.     # to avoid conflicts.
  90.      
  91.     GOBJ_NOTIFY_LEAK = false # when true, displays a list of undisposed graphical objects
  92.     # every time the scene changes.  This includes all graphical objects
  93.      
  94.     GOBJ_NOTIFY_CRITICAL = false # when true, displays information regarding critical
  95.     # graphical object disposal oversights on scene switch.  These are the errors
  96.     # that could otherwise turn into a Game.exe crash.
  97.      
  98.     # The above two options print a message directly to the screen.
  99.      
  100.     GOBJ_DEBUG_FILE = true # makes a file (gobj.txt) in the game directory containing
  101.     # information about new critcal objects whenever a scene switches
  102.     # the list includes:
  103.     # the time the error was recorded
  104.     # the object's class and ID
  105.     # the scene it was created during (NilClass = in a script before any scene was created)
  106.     # and the 'caller', or the list of methods run prior to this object's creation
  107.     # the first line on caller will generally be the location of where the
  108.     # offending object was initially CREATED
  109.     # HOWEVER, the error this script addresses is that this object is never DISPOSED
  110.     # of properly.  Knowing where the object will only allow a scripter to go back
  111.     # and properly dispose of the object at the correct time.
  112.      
  113.     GOBJ_LOG_NON_CRITICAL = false
  114.     # if set to true creates log entries for non-critical objects that are not disposed
  115.     # between scenes.  Only works if GOBJ_DEBUG_FILE is also set to true.
  116.     # if you have a game.exe crash that seems to pop up randomly after a while
  117.     # try using this and see if there are any unfreed objects at all
  118.      
  119.     GOBJ_LAZY = false
  120.     # turn this to true and graphical objects with disposed viewports will be disposed
  121.     # when the scene changes.  It is recommended this setting not be used and instead
  122.     # the code be cleaned up directly.
  123.     # v 1.1 This function has been superceeded by below.  
  124.     # Sprites must be kept in memory to prevent a crash if their viewport has already
  125.     # been disposed.
  126.     # Note this only seems to occur if screen redraws have occured between the time
  127.     # of viewport disposal and sprite disposal
  128.      
  129.     GOBJ_DEBUG_CRITICAL_DISPOSAL = false
  130.     # disables disposal of GO that have had their viewports already disposed
  131.     # this is only considered unsafe if screen redraws have taken place between the
  132.     # time that the viewport and sprite are disposed.
  133.     # Some of the base scripts dispose viewport immediately before the sprites, which
  134.     # has never been known to cause errors, therefore, this option has been added to
  135.     # circumvent dealing with these type of objects.  Turn this on if you continue
  136.     # to get Game.exe crashes that are not logged.
  137.      
  138.      
  139.     # --- End Setup
  140.       $gobj = []
  141.      
  142.      
  143.     [Sprite, Plane, Window, Tilemap].each { |cl|
  144.     class << cl
  145.       alias new_gobj new unless $@
  146.       def new(*args)
  147.         obj = new_gobj(*args)
  148.         ary = [obj, $scene.class]
  149.         ary.push(caller) if GOBJ_DEBUG_FILE # add caller list if debug file is enabled
  150.         # if object is disposed already during initialization, dont add it
  151.         $gobj.push(ary) unless obj.disposed?
  152.         obj
  153.       end
  154.      
  155.     end
  156.      
  157.     cl.class_eval {
  158.      
  159.       alias dispose_gobj dispose unless $@
  160.       def dispose
  161.         if GOBJ_DEBUG_CRITICAL_DISPOSAL && viewport && viewport.disposed?
  162.           o = $gobj.find { |a| a[0] == self }
  163.           print "#{o[0]} created in #{o[1]} is attempting to dispose with a disposed viewport!" if GOBJ_NOTIFY_CRITICAL
  164.           if GOBJ_DEBUG_FILE && !o[3]
  165.             gobj_log_to_file(o, true)
  166.             o[3] = true
  167.           end
  168.           return
  169.         end
  170.         gobj_exempt   # remove from global reference
  171.         dispose_gobj # original dispose
  172.       end
  173.      
  174.       def gobj_exempt
  175.         $gobj.delete_if { |a| a[0] == self }
  176.       end
  177.      
  178.     } # class eval
  179.      
  180.     } # each class
  181.      
  182.     class Scene_Base
  183.       alias main_gobj main unless $@
  184.       def main
  185.         if !@gobj && $TEST && $gobj.size > 0
  186.           p 'Live Graphical Object List:', $gobj.collect { |o| o[0..1] } if GOBJ_NOTIFY_LEAK
  187.           $gobj.clone.each { |o|
  188.           next o[0].gobj_exempt if o[0].disposed?
  189.           critical = o[0].viewport && o[0].viewport.disposed?
  190.           print "#{o[0]} created in #{o[1]} is a potential for Game.exe crash!" if GOBJ_NOTIFY_CRITICAL && critical
  191.           if GOBJ_DEBUG_FILE && !o[3] && (critical or GOBJ_LOG_NON_CRITICAL)
  192.             gobj_log_to_file(o, critical)
  193.             o[3] = true # do not log again this instance
  194.           end
  195.           if GOBJ_LAZY && critical
  196.             o[0].dispose
  197.           end
  198.           } # close $gobj.each
  199.           @gobj = true # once per run of this specfic scene object
  200.         end # debug branch
  201.         main_gobj  #original method
  202.       end
  203.      
  204.      
  205.     end
  206.      
  207.     module Kernel
  208.      
  209.       def gobj_log_to_file(o, critical)
  210.         File.open("gobj.txt", "a") { |f|
  211.         f.print "\n-----\n"
  212.         f.print("Time: #{Time.now}\n")
  213.         f.print("#{critical ? '' : 'Non-'}Critical Object #{o[0]}\n")
  214.         f.print("In Scene #{o[1]}\n")
  215.         f.print("Caller:: \n")
  216.         o[2].each { |e| e.gsub!(/Section(\d+)\:(\d+)/i) { |m|
  217.         "Script #{$1} -- #{ScriptNames[$1.to_i]}, Line: #{$2}" }
  218.         } # close o[2].each
  219.         outp = o[2].join("\n")
  220.         f.print(outp)
  221.         } # close file
  222.       end
  223.      
  224.     end
  225.      
  226.     class Viewport
  227.       alias dispose_gobj dispose unless $@
  228.       def dispose
  229.         @disposed = true
  230.         dispose_gobj
  231.       end
  232.      
  233.       def disposed?
  234.         @disposed
  235.       end
  236.      
  237.     end
  238.      
  239.     class Plane
  240.       # Plane#viewport methods do not work correctly
  241.       # Plane#viewport takes an argument and SETS the viewport
  242.       # while Plane#viewport= is not defined at all
  243.       # fixed to work like sprite, window and Tilemap
  244.      
  245.       alias viewport= viewport unless $@
  246.       alias viewport_set viewport unless $@
  247.       def viewport=(vp)
  248.         @viewport = vp
  249.         viewport_set(vp)
  250.       end
  251.      
  252.       def viewport
  253.         @viewport
  254.       end
  255.      
  256.       alias initialize_vpfix initialize
  257.       def initialize(vp = nil)
  258.         @viewport = vp
  259.         initialize_vpfix(vp)
  260.       end
  261.      
  262.     end
  263.      
  264.     ScriptNames = {}
  265.      
  266.     load_data("Data/Scripts.rvdata2").each_with_index {|s, i| ScriptNames[i] = s[1] }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement