SHOW:
|
|
- or go back to the newest paste.
| 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 = true | |
| 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.rvdata").each_with_index {|s, i| ScriptNames[i] = s[1] } |
| 266 | + | load_data("Data/Scripts.rvdata2").each_with_index {|s, i| ScriptNames[i] = s[1] } |