View difference between Paste ID: UNnUfahN and rW9qkbxT
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] }