Savegame data implimentation

Savegame data implimentation

Shanii2
Collaborator Collaborator
1,890 Views
9 Replies
Message 1 of 10

Savegame data implimentation

Shanii2
Collaborator
Collaborator

Hi everyone hope everyone is fine. I found some lua code based of save game data that i can hookup to the flownodes. but thing is where do i define the location to save the savegamedata.. i know I can save the table of any type of data numeric, boolean,strings,ids,vectors do i need to specify. the table for each data type and then stores these content tables to another table that goes in to savegame file. and how to retrieve these tables any indications is quite helpful. i got some hints but little is appreciated.

Reply
Reply
0 Likes
Accepted solutions (2)
1,891 Views
9 Replies
Replies (9)
Message 2 of 10

_robbs_
Alumni
Alumni

Hi,

 

If you're using the `stingray.SaveSystem` API, the directory is set by the <platform>.save_dir key in the settings.ini file.

 

For example

 

win32 = {
    save_dir = "C:\whatever"
}

This applies to Windows and iOS. It doesn't seem like that SaveSystem API is implemented on Android.

Reply
Reply
0 Likes
Message 3 of 10

_robbs_
Alumni
Alumni

There are a few special strings that you can use in the value, which will get expanded automatically

 

  • %APPDATA%: The folder for user storage in Windows.
  • %SAVEDATA%: The folder for user storage in iOS.
  • %HOSTNAME%: The name of the computer.
  • %DATE% or %UTCDATE%: The local or UTC date on the form YYYY-MM-DD.
  • %TIME% or %UTCTIME%: The local or UTC time on the form HH.MM.SS.
  • %RANDOM%: Eight random letters.
  • %SESSION%: Random hexadecimal string identifying this play session. This can be used to match different logs from same play session.
  • %STEAMUSER% for your user data folder if you're using Steam.
Reply
Reply
0 Likes
Message 4 of 10

Shanii2
Collaborator
Collaborator

Thanks and can you give a bit of hint how to save the game data in form of tables and to retrieve that data back...  because game data can be consist of number of things like. current score. achievement unlocked in form of boolean . special ids of things. the previous level as string. current position of character and current situation in the form of some sort of boolean or string..  i know i can create a table for each data type that can be saved but again how to retrieve the table back from saved data because the global variables only retains data during runtime.

Reply
Reply
0 Likes
Message 5 of 10

_robbs_
Alumni
Alumni
Accepted solution

Basically you're responsible for putting together a table that contains all the data you need for your game. So, for example, your table might contain a matrix for the player's last position/rotation in the world, a number that indicates the last level the player was on, and a table for each level in the game that contains a boolean value for each inventory item that the player can collect, to indicate whether it's been found.

 

Once you have your data ready, you call

stingray.SaveSystem.auto_save("filename", table)
to record your table to a file. As mentioned earlier, this gets saved to the location you set in the settings.ini file.

 

When you need to restore a game from saved data, you call

token = stingray.SaveSystem.auto_load("filename")

to get the data back from the file. This function returns a "token" ID that identifies your request.

 

This call is *asynchronous*, which means that it may take several frames to finish loading. So, after you call auto_load, you have to call

local progress_data = stingray.SaveSystem.progress(token)

and pass it your loading token. This function returns a table that tells you when the saved data is fully loaded. When progress.done is true, then you can get back the table you originally saved in your call to auto_save from progress.data.

 

 

Here's some code from an example project that shows it in practice.

First, the table is set up in SaveManager.data.

Every time the player completes a level, SaveManager.needToSave is set to true, and the next frame update the SaveManager.save_file function gets called, which calls SaveSystem.auto_save().

Every time a new game is started and the save system is initialized, it tries to load the same file in SaveManager.init. It saves the token in SaveManager.token. Then, each update, SaveManager.onProgress calls SaveSystem.progress() to check how the load is progressing. If the request is finished, and it has data, then it copies that data back to SaveManager.data.

 

Project.SaveManager = Project.SaveManager or {}

local SaveSystem = stingray.SaveSystem
local SaveManager = Project.SaveManager

SaveManager.data = SaveManager.data or {}

SaveManager.initDone = false
SaveManager.isFirst = true

SaveManager.needToSave = false

function SaveManager.enabled()
    return SaveSystem.exists()
end

function SaveManager.init()
    if not SaveManager.initDone then
        SaveManager.create_empty_data()
        if SaveManager.enabled() then
             print("Save Manager is ENABLED")
             SaveManager.load_file()
        else
            print("Save Manager is DISABLED")
        end
        SaveManager.initDone = true
    end
end

function SaveManager.create_empty_data()
    for nameCount = 1, 20 do
        SaveManager.data["Level_"..nameCount] = {
	                ["big_star_taken"] = false,
	                ["stars"] = 0,
	                ["locked"] = true
	   }
    end

    SaveManager.data["Level_1"]["locked"] = false
end

function SaveManager.is_level_locked(levelNumber)
    if not SaveManager.enabled() then return false end
    return SaveManager.data["Level_"..levelNumber]["locked"]
end
function SaveManager.is_star_collected(levelNumber)
    if not SaveManager.enabled() then return false end
    return SaveManager.data["Level_"..levelNumber]["big_star_taken"]
end

function SaveManager.get_stars_collected(levelNumber)
    if not SaveManager.enabled() then return 0 end
    return SaveManager.data["Level_"..levelNumber]["stars"]
end

function SaveManager.load_file()
    if not SaveManager.enabled() then return end
    SaveManager.token = SaveSystem.auto_load("data_file")
end

function SaveManager.save_file()
    if not SaveManager.enabled() then return end
    SaveManager.token = SaveSystem.auto_save("data_file", SaveManager.data)
end


function SaveManager.onProgress()
    if not SaveManager.enabled() then return end
--     if stingray.Keyboard.pressed(stingray.Keyboard.button_id("a")) then
--  	    SaveManager.data["level_1"]["stars"] = SaveManager.data["level_1"]["stars"]  + 1
--  	    SaveManager.save_file()
 	    
--  	end

    -- Save the file when invoked from other thread
    if (SaveManager.needToSave) then
        SaveManager.needToSave = false
        SaveManager.save_file()
        return
    end


    if (SaveManager.token) then
        local progress = SaveSystem.progress(SaveManager.token)

        if (progress and progress.done) then
            --print ("save - load done !")
    
            if (progress.error) then
                if (progress.error == "STINGRAY_SAVEDATA_ERROR_MISSING") then
                        -- File is missing - need to create new empty file and save it !
                        
                        SaveManager.create_empty_data()

                        SaveSystem.close(SaveManager.token)
                        SaveManager.token = nil
                        
                        SaveManager.save_file()
                        -- SaveManager.update_menu()
                        print ("save - load token closed")
                end
            else
                print ("File is Loaded OK...")

                -- Reading... only on loading
                if (progress.data) then
                    SaveManager.data = progress.data
                    -- SaveManager.update_menu()
                end
                
                print ("Stars "..SaveManager.get_stars_collected(1))

                SaveSystem.close(SaveManager.token)
                SaveManager.token = nil
                
            end
        end        
    end        
end

function SaveManager.shutdown()
    if not SaveManager.enabled() then return end

    if (SaveManager.token) then
        print ("Finallizing...")
        SaveSystem.close(SaveManager.token)
    end
end

function SaveManager.update_menu()
    if SimpleProject.level_name ~= "content/levels/Menu" or next(SaveManager.data) == nil then
        return false
    end
    
    local nora = stingray.World.unit_by_name(SimpleProject.world, "Character_Main2")
    if SaveManager.isFirst then
        stingray.Unit.flow_event(nora,"initPositionInMenu")
        SaveManager.isFirst = false
    else
        stingray.Unit.flow_event(nora,"positionInMenu")
    end
    
    for i = 1, 20 do
        local level_unit = stingray.World.unit_by_name(SimpleProject.world, "Level_" .. i)
        local locked = SaveManager.is_level_locked(i)
        local starCollected = SaveManager.is_star_collected(i)
        stingray.Unit.set_mesh_visibility(level_unit, "Lock", locked)
        stingray.Unit.set_mesh_visibility(level_unit, "Star_00", not locked)
        stingray.Unit.set_data(level_unit,"starCollected",starCollected)
        stingray.Unit.flow_event(level_unit,"updateStar");
        local stars = SaveManager.get_stars_collected(i)
        for j = 1, SaveManager.get_stars_collected(i) do
            stingray.Unit.set_mesh_visibility(level_unit, "Star_0" .. j, not locked)
        end
        for j = stars+1, 5 do
            stingray.Unit.set_mesh_visibility(level_unit, "Star_0" .. j, false)
        end
    end

    return true
end

-- level is the current level
function SaveManager.on_level_completed(level, numStars, hasBigStar)
    if (level  < 21) then
        SaveManager.data["Level_" .. level+1]["locked"] = false
    end    
    SaveManager.data["Level_" .. level]["stars"] = numStars
    SaveManager.data["Level_" .. level]["big_star_taken"] = hasBigStar
    SaveManager.needToSave = true
end

function SaveManager.get_max_levels_passed()
   if not SaveManager.enabled() then return 20 end

    for i = 1, 20 do
       if  (SaveManager.data["Level_"..i]["locked"] == true) then
           return i-1
        end 
    end
    
   return 20

end

return SaveManager

 

 

Reply
Reply
Message 6 of 10

Shanii2
Collaborator
Collaborator

wow i can see how its all put together. if i were to use custom flow nodes. can i use the variables similar to set_data() and get_data(). but instead of locating in global table it will look into the table of save game token. like for example set_data(weaponupgrade,mk51,4) and this varible be saved in the savegame table. then on retrieving the table from token i would use get_data(weoponupgrade,mk51,4) which will look into the table content of token instead of general lua global table. and then passed that value to set_unit_data(). Am i write. i would timely need your help as i test this myself.Thanks for great help. 

Reply
Reply
0 Likes
Message 7 of 10

_robbs_
Alumni
Alumni

Ah, one thing I forgot to mention in my last post is that after each auto_save or auto_load call is completed, you do need to call SaveSystem.close(token). This frees the memory used by your save/load request.

 

So, after you call close(), you won't be able to read or write data to that token anymore. It's best to do like the code example, and persist the token.data by copying it to another variable in your project.

Reply
Reply
0 Likes
Message 8 of 10

Shanii2
Collaborator
Collaborator

Yeah i grasp that so i need to call close token after i retrieved data or i should save the data table from token to a global table and then retrive data over time and pass the close call once the loading is completed 

Reply
Reply
0 Likes
Message 9 of 10

_robbs_
Alumni
Alumni
Accepted solution

You can call close() anytime after the loading has completed, and after you have copied its token.data.

Generally it's better to do this as soon as possible, since you won't need that token for anything after you've pulled out its data.

Reply
Reply
0 Likes
Message 10 of 10

Shanii2
Collaborator
Collaborator

Thanks alot in this regard

Reply
Reply
0 Likes