Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FlxSave: Add new LOAD_ERROR state and backupParser arg #3286

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from

Conversation

Geokureli
Copy link
Member

@Geokureli Geokureli commented Nov 15, 2024

Adds FlxSaveStatus.LOAD_ERROR which is an error that happened during the bind (load) process, where ERROR happened during the flush (save) process.

Alternative to #3270, any thoughts @EliteMasterEric ?

When the load error is a parsing error, the raw save data and parsing exception are available in the save's state. To repair the save, manually edit and/or unserialize it, wipe the old save and replace it with the new, repaired data

Example

package states;

import flixel.FlxG;
import flixel.util.FlxSave;
import haxe.Exception;
import haxe.Json;
import haxe.Serializer;
import haxe.Unserializer;
import openfl.net.SharedObject;

#if generateData
enum Values { A; B; }
#end

class SaveCrashTestState3270 extends flixel.FlxState
{
    inline static var INVALID_DATA = "oy8:someDatafy5:valuewy13:states.Valuesy1:B:0y9:otherDatay6:stringg";
    
    
    var save:FlxSave;
    
    override function create()
    {
        super.create();
        
        #if generateData
        // Used to get `INVALID_DATA`
        final data = { someData:false, value: Values.B, otherData:"string" };
        trace('serializing data${Json.stringify(data)}');
        final serialized = Serializer.run(data);
        trace('serialized data: ${serialized}');
        trace(Serializer.run({ someData:false, otherData:"string" }));
        #else
        runTest();
        #end
    }
    
    function runTest()
    {
        trace('Saving invalid data: "$INVALID_DATA"');
        saveRawData("test-save", INVALID_DATA);
        trace('loading "test-save"');
        save = new FlxSave();
        save.bind("test-save", resolveParsingError);
        switch save.status
        {
            case BOUND(_,_):
                trace('Bind successful: ${Json.stringify(save.data)}');
            case found:
                trace('Bind failed: $found');
        }
        trace('Erasing save');
        save.erase();
    }
    
    function resolveParsingError(rawData:String, e:Exception)
    {
        trace('Parsing failed, data:"$rawData", error:"$e"');
        
        trace("patching data");
        final reg = ~/y5:valuewy13:states.Valuesy.:.+:0/;
        final patchedData = reg.split(rawData).join("");
        
        trace('Resolving patched data: "$patchedData"');
        final unserializer = new Unserializer(patchedData);
        final resolver = { resolveEnum: Type.resolveEnum, resolveClass: FlxSave.resolveFlixelClasses };
        unserializer.setResolver(cast resolver);
        final parsedData = unserializer.unserialize();
        trace('resolving data: ${Json.stringify(parsedData)}');
        return parsedData;
    }
    
    @:access(openfl.net.SharedObject)
    @:access(flixel.util.FlxSave)
    function saveRawData(name:String, encodedData:String)
    {
        final meta = openfl.Lib.current.stage.application.meta;
        var path = meta["company"];
        if (path == null || path == "")
            path = "HaxeFlixel";
        else
            path = FlxSave.validate(path);
        
        final obj = SharedObject.getLocal(name, path);
        
        // put outdated data in save
        // copied from SharedObject.hx flush
        #if (js && html5)
            var storage = js.Browser.getLocalStorage();
            
            if (storage != null)
            {
                storage.removeItem(obj.__localPath + ":" + obj.__name);
                storage.setItem(obj.__localPath + ":" + obj.__name, encodedData);
            }
        #else
            var path = SharedObject.__getPath(obj.__localPath, obj.__name);
            var directory = haxe.io.Path.directory(path);
            
            if (!sys.FileSystem.exists(directory))
            {
                SharedObject.__mkdir(directory);
            }
            
            var output = sys.io.File.write(path, false);
            output.writeString(encodedData);
            output.close();
        #end
    }
}

@EliteMasterEric
Copy link
Contributor

Reading on my phone, but seems good to me. As long as there's a way to handle errors during parsing.

@Geokureli
Copy link
Member Author

Added an optional backupParser arg to bind. If unserializing fails, the backup parser is used, otherwise, same as my previous version, the save is stuck in an error state until you try binding again

@Geokureli Geokureli changed the title FlxSave: Add new LOAD_ERROR state FlxSave: Add new LOAD_ERROR state and backupParser arg Nov 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants