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: Allow custom handling of parsing errors #3286

Merged
merged 8 commits into from
Nov 25, 2024

Conversation

Geokureli
Copy link
Member

@Geokureli Geokureli commented Nov 15, 2024

Adds a backupParser arg to bind, which is called to handle parsing errors, also adds FlxSaveStatus.LOAD_ERROR which is an error that happened during the bind (load) process, where ERROR happened during the flush (save) process that was not resolved.

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)}');
                trace('Erasing save');
                save.erase();
            case found:
                trace('Bind failed: $found');
        }
    }
    
    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
@Geokureli Geokureli changed the title FlxSave: Add new LOAD_ERROR state and backupParser arg FlxSave: Allow custom handling of parsing errors Nov 24, 2024
@Geokureli Geokureli merged commit df19e4b into HaxeFlixel:dev Nov 25, 2024
11 checks passed
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