Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

[RGSS] About Marshal and Saving Methods in RMXP

* This whole thing were written in a post in RGSS Support, but i sense no one will read it lol... So, i decided to post it here, supposing it will help new scripters.



Almost everybody knows that RMXP has the ability to save the progress made by the player in any game. But just a few people really knows how it does such that thing. In fact, there´re a lot of Ruby´s default library code in RGSS that people don´t make any kind of idea about its existence. Marshal can be included in this group. Now let´s try to understand more on this library that´s the great responsible for the saving and loading data possibility in RMXP.


Marshal is a library that converts all data from an object/objects into a byte stream, permitting the scripter to save this object into a file (outside the Ruby Interpreter´s memory) and recover it later on. Some objects cannot be dumped: if the objects to be dumped include bindings, procedure objects, instances of class IO, or singleton objects, a TypeError will be raised, so take care with what you dump. I have tried once to save an entire Bitmap for example, and the game crashed xD

Usage is fairly simple. This library has only two methods and they´re simple and very straightforward.
  • Marshal.dump(object, [anIO, [limit]])
This method converts all the objects´ data into a byte stream. object is te object to be serialized.​
anIO is not necessary, but if passed it must be an instance of the IO class (this is an special object that can be saved as an ordinary file, for you to know). In this case, Marshal.dump will dump the object´s stream into this IO object. Otherwise, it will return a string with the serialized object.​
In Marshal.dump, more than one object can be dumped into an IO object, and in this case the user can make the process check to see the number of dumps made in this IO object. limit[/b] represents the maximum number of objects that can be inside the IO object. -1 is the default value, and if it is that value, no ckeck will be made. As anIO, this argument is not necessary.

  • Marshal.load(object, [proc])
Marshal.load is the part responsible for recovering the dumped object´s data. object must be an instance of IO class, a String or any class that responds to the to_str method.​
This method returns the recovered object. If an IO object is passed and if it has more than one object dumped, it will load the objects in the same order they were dumped (the first dumped object will be the first to be loaded), and will return only ONE object each time you ask for it to load objects from that IO class. This operation must be done in order, so take care.​
proc is a Proc object that can be passed to be executed each time you load an object. Don´t really care with it since it´s a bit complicated for most scripters ^^ (if you want i can explain about Procs later).​

So... The two methods are explained, so let´s take some examples for you to understand about them:

Suppose we have those two classes:
Code:
# Class that will hold some text...
[SIZE=2]class Text[/SIZE]
 
[SIZE=2]attr_accessor :data[/SIZE]
[SIZE=2]def initialize[/SIZE]
[SIZE=2] @data = "Hi"[/SIZE]
[SIZE=2]end[/SIZE]
 
[SIZE=2]end[/SIZE]
 
[SIZE=2]# Class that will be the real object we´ll use[/SIZE]
[SIZE=2]class Handler[/SIZE]
 
[SIZE=2]def initialize[/SIZE]
[SIZE=2] @text = Text.new[/SIZE]
[SIZE=2]end[/SIZE]
 
[SIZE=2]def say_something[/SIZE]
[SIZE=2] return @text.data[/SIZE]
[SIZE=2]end[/SIZE]
 
[SIZE=2]def record_this_text(txt)[/SIZE]
[SIZE=2] @text.data = txt[/SIZE]
[SIZE=2]end[/SIZE]
 
[SIZE=2]end[/SIZE]

So, we need new instances of them to start explaining anything.
Code:
myObject = Handler.new
[SIZE=2]print myObject.say_something     => "Hi"[/SIZE]
 
[SIZE=2]# Changing some data[/SIZE]
[SIZE=2]myObject.record_this_text("Hello World!")[/SIZE]

As you can see we changed the object´s data and wants to store it. Suppose we can´t do it with IO objects, the only way to do is with Strings:
Code:
# Serializing the object and getting the result
[SIZE=2]string = Marshal.dump(myObject)[/SIZE]

Now our object is serialized inside string. To recover it we call Marshal.load:
Code:
recoveredObject = Marshal.load(string)
[SIZE=2]print recoveredObject.say_something     => "Hello World!"[/SIZE]

As you can see it´s very easy. Now let´s get more in depth... We will save it into a file. First, create a new IO object for that:
Code:
filename = "Handler.rxdata"
[SIZE=2]mode = "wb"  # in this case the file will be in the write mode only[/SIZE]
[SIZE=2]file = File.open(filename, mode)[/SIZE]

* NOTE: File.open opens the file specified in filename and returns a IO object that contains the bytes of it. If the file doesn´t exists it creates a new file with the name passed. Note that filename must contain the complete path and filename, but in our case we just used the default Game folder (supposing we´re writing it in RMXP). So, the new file will appear in the same place where Game.exe is.

Pass it to Marshal.dump...
Code:
# In this case Marshal.dump returns nothing. 
[SIZE=2]Marshal.dump(myObject, file)[/SIZE]
 
[SIZE=2]# It´s REALLY important to close the IO object, otherwise[/SIZE]
[SIZE=2]# the object won´t be saved correctly. Make this after dumping[/SIZE]
[SIZE=2]# all the needed objects.[/SIZE]
[SIZE=2]file.close[/SIZE]

Now, to recover our object, first open the file:
Code:
filename = "Handler.rxdata"
[SIZE=2]mode = "rb"  # in this case the file will be in the read mode only[/SIZE]
[SIZE=2]file = File.open(filename, mode)[/SIZE]

Next, call Marshal.load:
Code:
recoveredObject = Marshal.load(file)
[SIZE=2]recoveredObject.say_something     => "Hello World!"[/SIZE]

The magic is done ^^

Now your saved and recovered myObject is in recoveredObject. And sure, you can dump more than one object into a file. Take RMXP for example:
SAVING:
Code:
    file = File.open(filename, "wb")
[SIZE=2] Marshal.dump($game_system, file)[/SIZE]
[SIZE=2] Marshal.dump($game_switches, file)[/SIZE]
[SIZE=2] Marshal.dump($game_variables, file)[/SIZE]
[SIZE=2] Marshal.dump($game_self_switches, file)[/SIZE]
[SIZE=2] Marshal.dump($game_screen, file)[/SIZE]
[SIZE=2] Marshal.dump($game_actors, file)[/SIZE]
[SIZE=2] Marshal.dump($game_party, file)[/SIZE]
[SIZE=2] Marshal.dump($game_troop, file)[/SIZE]
[SIZE=2] Marshal.dump($game_map, file)[/SIZE]
[SIZE=2] Marshal.dump($game_player, file)[/SIZE]
[SIZE=2] file.close[/SIZE]

LOADING:
Code:
    file = File.open(filename, "rb")
[SIZE=2] $game_system        = Marshal.load(file)[/SIZE]
[SIZE=2] $game_switches      = Marshal.load(file)[/SIZE]
[SIZE=2] $game_variables     = Marshal.load(file)[/SIZE]
[SIZE=2] $game_self_switches = Marshal.load(file)[/SIZE]
[SIZE=2] $game_screen        = Marshal.load(file)[/SIZE]
[SIZE=2] $game_actors        = Marshal.load(file)[/SIZE]
[SIZE=2] $game_party         = Marshal.load(file)[/SIZE]
[SIZE=2] $game_troop         = Marshal.load(file)[/SIZE]
[SIZE=2] $game_map           = Marshal.load(file)[/SIZE]
[SIZE=2] $game_player        = Marshal.load(file)[/SIZE]
[SIZE=2] file.close[/SIZE]

Note that it´s just the condensated and simplified Scene_Save and Scene_Load most important code. I gave focus only to the Marshal methods. Note that the files are saved and loaded at the same order, that´s very important.


Now, if your object can´t be saved by Marshal library bu default, you can create a serialization method for the specific object.
Will be done wait ^^
Also, for more informations about Marshal, check here. (Sorry i´m so tired, i will explain the serialization method in another time ^^)


And magic, our object is saved inside Handler.rxdata ^^

I REALLY hope you understand what i´m trying to explain, and i´m sorry if i was too obvious and pointless in any part.


P.S.: Give me some time and i´ll explain Serialization methods in depth.
 
Though hard to understand at points, the only thing I can see improved about this tutorial is possibly explaining key terms like "byte-stream" and "r" and "wb" and all those wonderful things. ^_^. Good job, t'was a good read.
 
mmh, nice.
but as Paradox said, it would be good to list the different reading-writing modes for IO. like r, +r, wb, w, rw, etc...

and to finish the third part about marshal methods.
def marshal_dump; return value; end;
def marshal_load; end
 
I think this part of the Help File is relevant:

The following built-in functions are defined in RGSS.

load_data(filename)​
Loads the data file indicated by filename and restores the object.

$data_actors = load_data("Data/Actors.rxdata")​

This function is essentially the same as:
File.open(filename, "rb") { |f| obj = Marshal.load(f) }​

However, it differs in that it can load files from within encrypted archives.

save_data(obj, filename)​
Saves the object obj to the data file indicated by filename.

save_data($data_actors, "Data/Actors.rxdata")​

This function is the same as:
File.open(filename, "wb") { |f| Marshal.dump(obj, f) }​

It is unlikely you will ever use this function during a game, but it is defined as a counterpart to load_data.
 
@Paradox: That will remain in another tutorial, just wait ^^

@Selwyn: Serialization is not exactly that... =p

@Me: Not exactly. In this case you may be referring to the mode of the IO object created to write the byte stream into, and this byte stream is provided by Marshal.dump. wb means that this IO object will be write-only, in binary mode. It doesn´t have anything to do with Marshal.

@Prexus: Thanx for your add-on ^^

@Chaosg1: Thx man! ^^
 
Ah I see. I know Marshal is not that IO thingie.. w/e

But, when I write to a file (saw wb, w+b, a+b or r+w+b w/e). Do I get the same output as if I was dumping it with Marshal?
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top