- Joined
- Jan 2, 2014
- Messages
- 1,790
- Reaction score
- 943
- First Language
- Chinese
- Primarily Uses
- N/A
The script of interest is Damage Sequence.
The below parts are what I'm talking about:
Now I've encountered a situation where I've to make a deep copy for a battler, and the only way I know is to use Marshal.
As the battler has an instance variable which is a sprite, Marshal will try to copy that sprite as well, which will lead to crashes as sprites can't be serialized.
To solve this, I'll have to use another variable to store that sprite and clear that instance variable of that battler right before using Marshal, then restore that instance variable of both the original and copied battler via the variable storing that sprite right after using Marshal.
Attempt 1
If only that sprite has issues with Marshal, I could do something like this:
However, I've also encountered similar issues with some other custom scripts as well. Using the above solution can lead to an exceptionally long list of instance variables to be cleared and restored due to the potentially ever increasing number of such custom scripts. Also, if any of those instance variables changes, my compatibility fix will have to change too.
Attempt 2
This motivates me to write a general solution that will automatically get rid of all instance variables that can't be serialized:
1. Use DoubleX RMVXA Object Trace to find all objects that can't be serialized and are linked to the object to be serialized -
Related parts of the implementation -
2. Use the below snippet to clear all those traced objects from the object to be serialized:
Test Case Version -
I used the below simplified test case:
Test Case 1 Specification -
Test Case 1 Result -
Test Case 2 Specification -
Test Case 2 Result -
Test Case 1 indicates that Attempt 1 worked while Attempt 2 failed.
While I know why the former worked, I don't know why the latter failed, nor any remote difference between them when it comes to clearing the test_battler instance variable @leds_sprite.
I tried several hours but I still have absolutely no clue at all, so I think I'll need help for this one(it seems to me that it's the hardest algorithm I've ever tried to solve)
The below parts are what I'm talking about:
class Spriteset_Battle def leds_update_actor_sprites $game_party.battle_members.each_with_index { |m,i| sprite = @actor_sprites next if sprite.nil? m.leds_sprite = sprite sprite.leds_battler = m } end end
Now I've encountered a situation where I've to make a deep copy for a battler, and the only way I know is to use Marshal.
As the battler has an instance variable which is a sprite, Marshal will try to copy that sprite as well, which will lead to crashes as sprites can't be serialized.
To solve this, I'll have to use another variable to store that sprite and clear that instance variable of that battler right before using Marshal, then restore that instance variable of both the original and copied battler via the variable storing that sprite right after using Marshal.
Attempt 1
If only that sprite has issues with Marshal, I could do something like this:
sprite = battler.leds_spritebattler.leds_sprite = nilclone = Marshal.load(Marshal.dump(battler))battler.leds_sprite = clone.leds_sprite = sprite
Attempt 2
This motivates me to write a general solution that will automatically get rid of all instance variables that can't be serialized:
1. Use DoubleX RMVXA Object Trace to find all objects that can't be serialized and are linked to the object to be serialized -
Related parts of the configuration -
# * Object manipulations |# 1. trace_obj(cond, label) |# - Traces all objects meeting cond method linked to this object |# - Labels all traced objects using label method |# - cond and label are method symbols in Object Trace Condition Method |# and Object Trace Label Method respectively |# 2. obj_trace[cond] |# - Returns all traced objects meeting cond method linked to this object |# - cond is a method symbol in Object Trace Condition Method |
# * Object manipulations |# 1. trace_obj(cond, label) |# - Traces all objects meeting cond method linked to this object |# - Labels all traced objects using label method |# - cond and label are method symbols in Object Trace Condition Method |# and Object Trace Label Method respectively |# 2. obj_trace[cond] |# - Returns all traced objects meeting cond method linked to this object |# - cond is a method symbol in Object Trace Condition Method |
Code:
#--------------------------------------------------------------------------| # Object Trace Condition Method | # - Setups cond used by trace_obj(cond, label) | #--------------------------------------------------------------------------| # cond must be the symbol of a method taking the currently traced object as # the only arguement # The below examples are added to help you setup your own cond methods # Checks if the currently traced object belongs to klass def self.cond_klass(obj) obj.is_a?(Sprite) end # cond_klass
Code:
#--------------------------------------------------------------------------| # Object Trace Label Method | # - Setups label used by trace_obj(cond, label) | #--------------------------------------------------------------------------| # label must be the symbol of a method taking the currently traced object as # the only arguement # The below examples are added to help you setup your own label methods # Labels all traced objects using their class symbol def self.label_klass(obj) obj end # label_klass
class Object # Edit #----------------------------------------------------------------------------| # New public instance variable | #----------------------------------------------------------------------------| attr_reader
bj_trace # The traces of all objects linked to this object # (v1.01a+)The list of symbols of all instance variables added by this script OBJ_TRACE_IVAR = [:"@obj_trace"] # cond: The object trace condition method symbol taking the object as argument # label: The object trace label method symbol taking the object as argument def trace_obj(cond, label) # New # Stop tracing the object if the object trace path would be cyclic (@obj_trace ||= {})[cond] ? return : @obj_trace[cond] = {} # trace_instance_obj(cond, label) return trace_array_obj(cond, label) if is_a?(Array) return trace_hash_obj(cond, label) if is_a?(Hash) return trace_range_obj(cond, label) if is_a?(Range) trace_struct_obj(cond, label) if is_a?(Struct) end # trace_obj # cond: The object trace condition method symbol taking the object as argument # label: The object trace label method symbol taking the object as argument def trace_instance_obj(cond, label) # New (instance_variables - OBJ_TRACE_IVAR).each { |ivar| trace_all_obj(cond, label, ivar, instance_variable_get(ivar)) } end # trace_instance_obj # cond: The object trace condition method symbol taking the object as argument # label: The object trace label method symbol taking the object as argument def trace_array_obj(cond, label) # New each_with_index { |val, index| trace_all_obj(cond, label, index, val) } end # trace_array_obj # cond: The object trace condition method symbol taking the object as argument # label: The object trace label method symbol taking the object as argument def trace_hash_obj(cond, label) # New each { |key, val| trace_all_obj(cond, label, key, val) } end # trace_hash_obj # cond: The object trace condition method symbol taking the object as argument # label: The object trace label method symbol taking the object as argument def trace_range_obj(cond, label) # v1.00b+; New # Embeds the klass traces of all ranges linking to this object index = -1 each { |val| trace_all_obj(cond, label, index += 1, val) } # end # trace_range_obj # cond: The object trace condition method symbol taking the object as argument # label: The object trace label method symbol taking the object as argument def trace_struct_obj(cond, label) # v1.00b+; New each_pair { |key, val| trace_all_obj(cond, label, key, val) } end # trace_struct_obj #----------------------------------------------------------------------------| # Label and use all nonempty subtrees to form the original object trace tree| #----------------------------------------------------------------------------| # cond: The object trace condition method symbol taking the object as argument # label: The object trace label method symbol taking the object as argument # iks: The index/key/symbol of the object trace # val: The object to be traced def trace_all_obj(cond, label, iks, val) # v1.01a+; New # Recursively traverse the object trace tree using Depth First Search ot = DoubleX_RMVXA:
bj_Trace @obj_trace[cond][iks] = [ot.send(label, val)] if ot.send(cond, val) val.trace_obj(cond, label) return if (trace = val.obj_trace[cond]).empty? (@obj_trace[cond][iks] ||= []) << trace # end # trace_all_objend # Object
Original algorithm implementation -
class Object #----------------------------------------------------------------------------| # Clears all traced objects meeting cond method linked to this object | #----------------------------------------------------------------------------| # tree: All traced objects meeting cond method linked to this object def clear_obj(tree) # New @clear_obj ? return : @clear_obj = true # Recursively traverse the object trace tree using Depth First Search tree.each { |iks, val| if iks.is_a?(Symbol) && instance_variable_get(iks) instance_variable_get(iks).clear_obj(val[-1]) instance_variable_set(iks, nil) if val.size > 1 elsif is_a?(Array) || is_a?(Hash) || is_a?(Struct) self[iks].clear_obj(val[-1]) self[iks] = nil if val.size > 1 end } # @clear_obj = nil end # clear_obj #----------------------------------------------------------------------------| # Restores all traced objects meeting cond method linked to this object | #----------------------------------------------------------------------------| # tree: All traced objects meeting cond method linked to this object def restore_obj(tree) # New @restore_obj ? return : @restore_obj = true # Recursively traverse the object trace tree using Depth First Search tree.each { |iks, val| if iks.is_a?(Symbol) && instance_variable_get(iks) instance_variable_set(iks, val[0]) if val.size > 1 instance_variable_get(iks).restore_obj(val[-1]) elsif is_a?(Array) || is_a?(Hash) || is_a?(Struct) self[iks] = val[0] if val.size > 1 self[iks].restore_obj(val[-1]) end } # @restore_obj = nil end # restore_objend # Object
class Object #----------------------------------------------------------------------------| # Clears all traced objects meeting cond method linked to this object | #----------------------------------------------------------------------------| # tree: All traced objects meeting cond method linked to this object def clear_obj(tree) # New @clear_obj ? return : @clear_obj = true # Recursively traverse the object trace tree using Depth First Search tree.each { |iks, val| if iks.is_a?(Symbol) && instance_variable_get(iks) instance_variable_get(iks).clear_obj(val[-1]) instance_variable_set(iks, nil) if val.size > 1 elsif is_a?(Array) || is_a?(Hash) || is_a?(Struct) self[iks].clear_obj(val[-1]) self[iks] = nil if val.size > 1 end } # @clear_obj = nil end # clear_obj #----------------------------------------------------------------------------| # Restores all traced objects meeting cond method linked to this object | #----------------------------------------------------------------------------| # tree: All traced objects meeting cond method linked to this object def restore_obj(tree) # New @restore_obj ? return : @restore_obj = true # Recursively traverse the object trace tree using Depth First Search tree.each { |iks, val| if iks.is_a?(Symbol) && instance_variable_get(iks) instance_variable_set(iks, val[0]) if val.size > 1 instance_variable_get(iks).restore_obj(val[-1]) elsif is_a?(Array) || is_a?(Hash) || is_a?(Struct) self[iks] = val[0] if val.size > 1 self[iks].restore_obj(val[-1]) end } # @restore_obj = nil end # restore_objend # Object
class Objectdef clear_obj(tree) # NewFile.open("diagnose.txt", "a") { |file|file.print("\npre clear_obj - tree: ", tree, " @clear_obj: ", @clear_obj, "\n")}@clear_obj ? return : @clear_obj = truetree.each { |iks, val|File.open("diagnose.txt", "a") { |file|file.print("clear_obj - iks: ", iks, " val: ", val, "\n")}if iks.is_a?(Symbol) && instance_variable_get(iks)File.open("diagnose.txt", "a") { |file|file.print("pre clear_obj - iks: ", iks, " instance_variable_get(iks): ", instance_variable_get(iks), "\n")}instance_variable_get(iks).clear_obj(val[-1])next if val.size <= 1instance_variable_set(iks, nil)File.open("diagnose.txt", "a") { |file|file.print("post clear_obj - iks: ", iks, " instance_variable_get(iks):", instance_variable_get(iks), "\n")}elsif is_a?(Array) || is_a?(Hash) || is_a?(Struct)File.open("diagnose.txt", "a") { |file|file.print("pre clear_obj - iks: ", iks, " self[iks]: ", self[iks], "\n")}self[iks].clear_obj(val[-1])next if val.size <= 1self[iks] = nilFile.open("diagnose.txt", "a") { |file|file.print("post clear_obj - iks: ", iks, " self[iks]: ", self[iks], "\n")}end}File.open("diagnose.txt", "a") { |file|file.print("post clear_obj - tree: ", tree, " @clear_obj: ", @clear_obj, "\n")}@clear_obj = nilend # clear_objdef restore_obj(tree) # NewFile.open("diagnose.txt", "a") { |file|file.print("\npre restore_obj - tree: ", tree, " @restore_obj: ", @restore_obj, "\n")}@restore_obj ? return : @restore_obj = truetree.each { |iks, val|File.open("diagnose.txt", "a") { |file|file.print("restore_obj - iks: ", iks, " val: ", val, "\n")}if iks.is_a?(Symbol) && instance_variable_get(iks)instance_variable_set(iks, val[0]) if val.size > 1File.open("diagnose.txt", "a") { |file|file.print("restore_obj - iks: ", iks, " instance_variable_get(iks): ", instance_variable_get(iks), "\n")}instance_variable_get(iks).restore_obj(val[-1])elsif is_a?(Array) || is_a?(Hash) || is_a?(Struct)self[iks] = val[0] if val.size > 1File.open("diagnose.txt", "a") { |file|file.print("restore_obj - iks: ", iks, " self[iks]: ", self[iks], "\n")}self[iks].restore_obj(val[-1])end}File.open("diagnose.txt", "a") { |file|file.print("post restore_obj - tree: ", tree, " @restore_obj: ", @restore_obj, "\n")}@restore_obj = nilend # restore_objend # Object
I used the below simplified test case:
Test Case Situation Replication -
test_battler = Game_Battler.newtest_sprite = Sprite_Battler.new(Viewport.new)test_battler.leds_sprite = test_spritetest_sprite.leds_battler = test_battler
test_battler = Game_Battler.newtest_sprite = Sprite_Battler.new(Viewport.new)test_battler.leds_sprite = test_spritetest_sprite.leds_battler = test_battler
test_battler.leds_sprite = nilFile.open("diagnose.txt", "a") { |file| file.print("Test Case 2:\n") file.print("test_battler.leds_sprite = nil\n") file.print("test_battler.leds_sprite: ", test_battler.leds_sprite, "\n") file.print("test_sprite.leds_battler.leds_sprite: ", test_sprite.leds_battler.leds_sprite, "\n")}test_battler.trace_obj
cond_klass, :label_klass)File.open("diagnose.txt", "a") { |file| file.print("test_battler.obj_trace[:cond_klass]: ", test_battler.obj_trace[:cond_klass])}
Test Case 1:test_battler.leds_sprite = niltest_battler.leds_sprite:test_sprite.leds_battler.leds_sprite:test_battler.obj_trace[:cond_klass]: {}
test_battler.trace_obj
cond_klass, :label_klass)File.open("diagnose.txt", "a") { |file| file.print("Test Case 1:\n") file.print("test_battler.obj_trace[:cond_klass]: ", test_battler.obj_trace[:cond_klass], "\n") file.print("================================================================================")}test_battler.clear_obj(test_battler.obj_trace[:cond_klass])File.open("diagnose.txt", "a") { |file| file.print("================================================================================\n") file.print("test_battler.leds_sprite: ", test_battler.leds_sprite, "\n") file.print("test_sprite.leds_battler.leds_sprite: ", test_sprite.leds_battler.leds_sprite, "\n")}test_battler.obj_trace.cleartest_battler.trace_obj
cond_klass, :label_klass)File.open("diagnose.txt", "a") { |file| file.print("\ntest_battler.obj_trace.clear\n") file.print("test_battler.obj_trace[:cond_klass]: ", test_battler.obj_trace[:cond_klass])}
Test Case 2:test_battler.obj_trace[:cond_klass]: {
leds_sprite=>[#<Sprite_Battler:0x4e093c8>, {
leds_battler=>[{...}]}],
result=>[{
battler=>[{...}]}]}================================================================================pre clear_obj - tree: {
leds_sprite=>[#<Sprite_Battler:0x4e093c8>, {
leds_battler=>[{...}]}],
result=>[{
battler=>[{...}]}]} @clear_obj: clear_obj - iks: @leds_sprite val: [#<Sprite_Battler:0x4e093c8>, {
leds_battler=>[{
leds_sprite=>[...],
result=>[{
battler=>[{...}]}]}]}]pre clear_obj - iks: @leds_sprite instance_variable_get(iks): #<Sprite_Battler:0x4e093c8>pre clear_obj - tree: {
leds_battler=>[{
leds_sprite=>[#<Sprite_Battler:0x4e093c8>, {...}],
result=>[{
battler=>[{...}]}]}]} @clear_obj: clear_obj - iks: @leds_battler val: [{
leds_sprite=>[#<Sprite_Battler:0x4e093c8>, {
leds_battler=>[...]}],
result=>[{
battler=>[{...}]}]}]pre clear_obj - iks: @leds_battler instance_variable_get(iks): #<Game_Battler:0x4e09814>pre clear_obj - tree: {
leds_sprite=>[#<Sprite_Battler:0x4e093c8>, {
leds_battler=>[{...}]}],
result=>[{
battler=>[{...}]}]} @clear_obj: truepost clear_obj - tree: {
leds_battler=>[{
leds_sprite=>[#<Sprite_Battler:0x4e093c8>, {...}],
result=>[{
battler=>[{...}]}]}]} @clear_obj: truepost clear_obj - iks: @leds_sprite instance_variable_get(iks):clear_obj - iks: @result val: [{
battler=>[{
leds_sprite=>[#<Sprite_Battler:0x4e093c8>, {
leds_battler=>[{...}]}],
result=>[...]}]}]pre clear_obj - iks: @result instance_variable_get(iks): #<Game_ActionResult:0x4e09738>pre clear_obj - tree: {
battler=>[{
leds_sprite=>[#<Sprite_Battler:0x4e093c8>, {
leds_battler=>[{...}]}],
result=>[{...}]}]} @clear_obj: clear_obj - iks: @battler val: [{
leds_sprite=>[#<Sprite_Battler:0x4e093c8>, {
leds_battler=>[{...}]}],
result=>[{
battler=>[...]}]}]pre clear_obj - iks: @battler instance_variable_get(iks): #<Game_Battler:0x4e09814>pre clear_obj - tree: {
leds_sprite=>[#<Sprite_Battler:0x4e093c8>, {
leds_battler=>[{...}]}],
result=>[{
battler=>[{...}]}]} @clear_obj: truepost clear_obj - tree: {
battler=>[{
leds_sprite=>[#<Sprite_Battler:0x4e093c8>, {
leds_battler=>[{...}]}],
result=>[{...}]}]} @clear_obj: truepost clear_obj - tree: {
leds_sprite=>[#<Sprite_Battler:0x4e093c8>, {
leds_battler=>[{...}]}],
result=>[{
battler=>[{...}]}]} @clear_obj: true================================================================================test_battler.leds_sprite: test_sprite.leds_battler.leds_sprite: test_battler.obj_trace.cleartest_battler.obj_trace[:cond_klass]: {
result=>[{
battler=>[{
leds_sprite=>[#<Sprite_Battler:0x4e093c8>, {
leds_battler=>[{...}]}],
result=>[{...}]}]}]}
Test Case 1 indicates that Attempt 1 worked while Attempt 2 failed.
While I know why the former worked, I don't know why the latter failed, nor any remote difference between them when it comes to clearing the test_battler instance variable @leds_sprite.
I tried several hours but I still have absolutely no clue at all, so I think I'll need help for this one(it seems to me that it's the hardest algorithm I've ever tried to solve)
Last edited by a moderator:
