Can there be a problem with the way this save data is handled?

● ARCHIVED · READ-ONLY
Started by Tsukihime 2 posts View original ↗
  1. I've been looking at a strange issue related to save files and a compatibility issue with one of my scripts.

    I eventually figured out what was wrong and think it's something that might be useful to share. The problem is related to how I chose to implement my own script (and therefore there wouldn't be an issue otherwise).

    Here's a method in DataManager that creates a save header.

    The save header is used by the save and load scenes to display "header" information. This is what it uses to draw previews for your save games (like your party members and playtime)

    def self.make_save_header header = {} header[:characters] = $game_party.characters_for_savefile header[:playtime_s] = $game_system.playtime_s header[:system] = Marshal.load(Marshal.dump($game_system)) header[:timer] = Marshal.load(Marshal.dump($game_timer)) header[:message] = Marshal.load(Marshal.dump($game_message)) header[:switches] = Marshal.load(Marshal.dump($game_switches)) header[:variables] = Marshal.load(Marshal.dump($game_variables)) header[:self_switches] = Marshal.load(Marshal.dump($game_self_switches)) header[:actors] = Marshal.load(Marshal.dump($game_actors)) header[:party] = Marshal.load(Marshal.dump($game_party)) header[:troop] = Marshal.load(Marshal.dump($game_troop)) header[:map] = Marshal.load(Marshal.dump($game_map)) header[:player] = Marshal.load(Marshal.dump($game_player)) headerendHere's the top half of a method defined in a window class that draws the save preview. In particular, it draws the characters in your party for that save file. The rest of the code that I've taken out is just more position calculations and draw calls.

    Code:
    def draw_save_characters(dx, dy)  return if @header[:party].nil?  reset_font_settings  make_font_smaller  dw = (contents.width - dx) / @header[:party].max_battle_members  dx += dw/2  for member in @header[:party].battle_members    next if member.nil?    member = @header[:actors][member.id]    change_color(normal_color)    draw_actor_graphic(member, dx, dy)    # more code
    For context, in certain parts of the default engine, objects are being cloned and operated on, and this was causing problems with my script because I was relying on certain fields from the object as hash keys, but because clones are being made, I couldn't distinguish between the original object and a cloned object, and one of my requirements is that cloned objects are not the same as the original object and should be stored as separate entries. If you had 10 clones, then you would create an extra entry for each one.For compatibility purposes as well as backwards compatibility with default scripts (which makes life harder and leads to questionable design decisions), I didn't exactly want to go and change the way the cloning was performed, because this just means any script that performs cloning on the particular objects I'm working with would be vulnerable to the same bug.

    So I went and looked for something that was guaranteed to be unique without me having to explicitly make it unique, and figured I'll just use ruby's object ID's as a unique identifier. Every object in ruby has a unique ID that the ruby interpreter uses for whatever purposes. Even if you're a clone, you still have a unique ID, and so I decided to use that as my hash key.

    Granted, this isn't necessarily a good idea. It might even be a bad idea since this data is being stored in a save file, as I'm exposing myself to even more bugs since now I'm working with data that is unpredictable(?).

    Now, my script ran into problems with the script where I extracted those snippets above because of the way the save data was being handled.

    Looking at just these two snippets, can you see any potential issues, given that I rely on object ID's for lookups?

    EDIT: looking at my code again, I'm making a pretty bad assumption here by working with object ID's. It would be easier to try to find a better solution that doesn't involve object ID's.
  2. To make a hash considering different objects as different keys, this method might help: Hash#compare_by_identity

    As far as I know, object identifiers are only guaranteed to be unique as long as their respective objects exist since Ruby can recycle unused object ids.

    I wonder why in the first method each game objects is "dumploaded" before it is added to the hash. After all these objects will be marshalled anyway during serialization, so all what is achieved is breaking dependencies caused by shared references between the objects.

    There shouldn't be any of those references by default, but since the VXAce introduced storing the game objects altogether there might be users who rely on it.