SHARE
TWEET

Graphical Object Global Reference ACE

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