What doesn't work with C3's Save and Load?

Not favoritedFavorited Favorited 0 favourites
From the Asset Store
Custom Loading Icons to Enhance Your Loading Screens
  • I've been finding weird quirks and pieces of code that don't work with the Save and Load and I'm trying to compile a list in order to avoid these problems.

    A lot have been reported and fixed, while some are difficult to fix (e.g. github.com/Scirra/Construct-bugs/issues/9052 )

    I would expect the following to have some sort of problem but I've yet to test and/or discover any:

    • complex JS
    • heavy/slow loops especially with variables
    • complex hierarchies
    • complex timelines
    • complex flowcharts
    • some 3D stuff

    I'm not interested to hear "make your own system" since I've already done that, but when I participate in a game jam, I want to use C3's one.

    I'd like to know if anyone else has found info on what NOT to use since based on the manual, everything should work fine.

    Some of these are confirmed limitations, while others are user-reported edge cases that still need minimal reproduction projects before they can be treated as confirmed Construct bugs.

    Confirmed / expected limitations:

    1. Drawing Canvas pixel data — confirmed by Ashley: the Drawing Canvas instance is saved, but the pixel data inside it is not.
    2. Images loaded from URLs — mentioned by R0J0hound as not saved automatically.
    3. Sounds loaded from URLs — mentioned by R0J0hound as not saved automatically.
    4. Mid-flight network/file requests — confirmed/expected limitation mentioned by Ashley and R0J0hound.
    5. External JavaScript state — mentioned by R0J0hound / Nindool: JS state outside Construct's runtime needs to be saved manually.

    Reported by users as not working / causing problems:

    1. Wait for previous action to finish — reported by me as causing issues.
    2. Tweens during cutscenes — reported by andreyin: saving/loading mid-cutscene can break when waiting for tween movement to finish.
    3. NPC movement using Tweens — reported by andreyin: loading a save made around tween-based NPC movement sometimes caused an error and made the save unusable.
    4. Hierarchies in UI / battle menus — reported by andreyin: loading a mid-battle save broke menu hierarchies and attached/shadow objects.
    5. Dynamically created/destroyed objects with instance variables — reported by Nindool as inconsistent on load in some cases.
    6. Global variables changed inside functions called from timelines — reported by Nindool as sometimes loading off by a step or two.

    Suspected / hinted as potentially risky, but not directly confirmed:

    1. Some async operations mid-progress — suggested by R0J0hound as an area to be careful with.
    2. Saving during timelines / mid-execution timeline logic — hinted by Nindool's report, but needs a minimal repro.
    3. Worker-based async tasks, e.g. pathfinding — suggested by R0J0hound as something that may not save cleanly if mid-progress.

    Tagged:

  • With JavaScript you’d have to save/load state manually with some api callbacks I think.

    Drawing canvas pixels and images/sounds loaded from urls aren’t saved either. You’d need some additional logic to save/load those.

    Everything else you listed should work fine. Things like hierarchies, timelines, flowcharts, etc… are just state and should save/load just fine barring any bugs.

    Event stuff like loops and variables will either not need to be saved/loaded or will save/load just fine.

    Sounds like the only thing you need to look at is async stuff. The file/network stuff makes sense to not work. Stuff running in a worker like finding a path makes sense that they wouldn’t work too.

    That said “wait for previous” should work with the “wait” and “wait for signal” actions since that is serialized.

    I don’t have a hard fast rule for all the async actions. They should be easy to identify since construct labels them all. I’d suspect a fair amount of them can save mid progress though.

    You could add some bookkeeping events around those actions to keep track of how many async things are running, and maybe when you want to save/load set a variable that prevents any new ones to start and wait till all the pending ones to complete before saving/loading. Just an idea.

    Anyways, just some thoughts.

  • I know you don't wanna hear it but make your own save system...

    My personal opinion on the save feature is still that it is prone to random issues and not adequate for saving a game simply because it's a savestate and not an actual save file. It's neat for a beginner to not have to think about how saving a game works but that's about it. The save feature just saves EVERYTHING and anything you don't want saved needs a specific behavior attached and that's the only control you have over it.

    It's just infinitely better to create a clean save with some json and save/load that in a proper and controlled way. There's of course some extra work involved but that work is worth every second, and in the end it is not actually that hard to do.

    Just for a good example: Let's say you publish a game on steam using the save feature and it's a success... how do you actually create an update for your game without completely bricking every single save file? It sounds like an absolute nightmare to handle this.

    Of course for a game jam it's neat to be able to use this as a quick and dirty way, but it's clear how insanely complex it is to actually create these type of savestates without bugs. It's just more headache than it's worth.

  • I have made my own save system, but I still use the save and load functions a lot for debugging. One of the projects I'm currently working on is an RPG, so I use it to save mid-cutscenes, or in the middle of a battle, or sometimes even before I reach a boss or start a battle. In all of these cases I have issues :

    1. Mid-cutscenes -> I have tried using the timeline feature and just couldn't wrap my head around it, so I made a cutscene system where when a cutscene gets called, we stop player movement and enable a group with the cutscene name - in this group, we have a ton of waits, or stuff like waiting for a character to finish a walking tween so the next part gets called. This generally works, but sometimes it doesn't, and I believe it has to do with tweens.

    2. Mid-Battle -> The battle menu that I have has hierarchies. When loading the save state, the hierarchies break, so the menu is broken, and shadows that were created when I spawned the battle sprites are also broken, etc.

    3. I have NPCs that walk around using tweens. I decided on using tweens because the Tile Movement behavior doesn't account for the bounding box. In any case, sometimes I save the game before I test something, and when I load I get an error, making the savestate unuseable.

    I absolutely agree that the save/load actions should not be used for actual save games and ideally we all should make our own save systems, but I think C3 shouldn't break when using it, and all default addons should work properly with it, or if it's impossible, it should be mentioned in the manual, so at least we can refer to it and workaround having to use it.

    Edit : That being said I think these issues should be reported on the bug tracker instead of talking about them in a random forum post. At least I think that people who will run into bugs later can refer to this to understand why they keep getting errors on the devtool when trying to load a saved game.

  • I've been finding weird quirks and pieces of code that don't work with the Save and Load and I'm trying to compile a list in order to avoid these problems.

    A lot have been reported and fixed, while some are difficult to fix (e.g. github.com/Scirra/Construct-bugs/issues/9052 )

    I would expect the following to have some sort of problem but I've yet to test and/or discover any:

    • complex JS
    • heavy/slow loops especially with variables
    • complex hierarchies
    • complex timelines
    • complex flowcharts
    • some 3D stuff

    I'm not interested to hear "make your own system" since I've already done that, but when I participate in a game jam, I want to use C3's one.

    I'd like to know if anyone else has found info on what NOT to use since based on the manual, everything should work fine.

    Current issues that I'm aware of:

    1. Using Wait for previous action to finish

    Thank you

    Good thread to have. From what I've run into, instance variables on objects that are created and destroyed dynamically can be inconsistent on load if you're not explicitly saving their state before destruction. Worth being careful with any object that has a short lifecycle.

    Also had issues with global variables that are modified inside functions called from timelines — the save snapshot doesn't always capture mid-execution state cleanly, so if a save triggers during a timeline the loaded state can be off by a step or two.

    For JS specifically, any external state or module-level variables won't be captured by the save system at all since it only tracks C3's own runtime data. So if you're storing anything important in JS variables rather than C3 instance/global variables it'll be lost on load.

  • To the extent feasible, the entire engine state is saved and loaded. It should cover virtually everything, with the sole exceptions being that linked issue, things like mid-flight network requests that are not possible to save, and things like Drawing Canvas pixels on the basis it's a large amount of data that you may well not want to include. The savegame system should be pretty bulletproof - it's been used widely for many years now, and we've fixed a bunch of bugs over the years.

    If something isn't working right, as ever please file an issue so we can fix it. I'd also point out often problems turn out to be mistakes in the project or misunderstandings - if you think the savegame system isn't covering something, prove it! Make a minimal project to test it and file an issue if necessary. I think the state of the feature is better than is portrayed in this thread but I am willing to be proven otherwise with issue reports, which we would then fix to make sure it works reliably.

    In my view it really doesn't make sense to tell people not to use a core engine feature and invent their own. Why did we bother making the whole feature if people are going to tell other people not to use it and to make their own? If there is something sub-par about the built-in feature, we should fix it so it does the job its meant to.

    Having said that, the savegame feature does include the full state of the gameplay and that can in some cases be a lot of data. The only time I'd agree it's fine to make your own save system is if it's very simple. For example if all you need to save is something like "reached level 5, current score is 120", then the savegame system is probably overkill, and you can just do something like save a small Dictionary with a couple of keys (or your own JSON data). If you want to include all the objects in the layout though, definitely use the built-in savegame feature - there is an absolute ton of stuff to take in to consideration if you want to handle that yourself, but we've already taken care of all that for you.

  • Ashley I agree the built-in system should be used, and I did try to use it properly. I'd rather not pay for a game engine to not use it's features, I don't want my life to be harder.

    I made heavy use of the No Save behavior to keep things simple, but I kept running into issues. A lot of them were fixed, but I keep finding more, either bugs, edge cases, or weird quirks.

    For proper game dev, I need a save system I can fully trust, not one that works correctly "most of the time". I really do not need more headaches, and I'm sure you can understand that.

    I've been making games with C2 and C3 for around 12 years now, full-time. I've also talked to many other devs who ended up making their own save systems too. I don’t think we can all be wrong about this.

    About proving specific issues: I understand why clean bug reports are needed but it gets very time consuming. I report one issue, then after a few C3 updates I run into another one, then another, and so on.

    At this point, if i can find a workaround, I usually dont bother reporting the bug anymore. Narrowing it down can take a lot of time, and if I can’t isolate it fast enough I usually just move on.

    What i'm trying to learn now is what to avoid in C3 and what reliable workarounds other devs are using. That's why I made this post. And if there was info in the manual about it, I wouldn't be here wasting our time on this.

    Also, just to confirm: is the Drawing Canvas object not saved by the system?

    If it is not saved, I don't really understand why that is the default behavior. My expectation was that objects are saved unless you attach the No Save behavior, since that seems to be the purpose of that behavior in the first place??

    Another big issue is that the manual does not seem to mention this at all. Imagine making a game, using the built-in save system, and only later discovering that Drawing Canvas data is not saved. If I check the manual and it says nothing about that, it is very easy to make a bad assumption.

    I'm also not interested in handling this through network requests since that would be foolish of me. I've made enough multiplayer games to know saving such a thing is pointless.

  • WackyToaster you're right, I don't want to talk about making my own save system here because I have one already, which is something I stated already. And yes the current Save/Load C3 has makes saves games unusable if you add more stuff to it.

  • I've said that once before but I think this system would work better if it would be the other way around: save NOTHING by default until you start adding a "do save" behavior. Or maybe both, so you could pick which way is better suited for your project.

    Here's some reasons why I'd not want to use it:

    1. Updating the game and loading a savestate from an old version sounds extremely iffy, especially if the loaded objects have changed for whatever reason

    2. Savestates potentially save (and load) broken game states (e.g. a softlock)

    3. Savestates are extremely unwieldy and the only control I have over it is the no-save behavior.

    What I mean by unwieldy: This is what a savestate in my current game would look like:

    Mind you I ran it through a beautifier... 27000 lines of JSON? I wanted to paste all of it here for emphathis, but the forum doesn't allow this many lines of code. And sure, I could cut it down by starting to apply the nosave behavior, but still. And my game is super tiny compared to some of the behemoth projects I've seen floating around.

    Here's what I'm actually, manually saving in my game.

    	globalThis.savegame = {
     "current_level": runtime.globalVars.current_level,
     "current_world": runtime.globalVars.current_world,
     "game_finished": runtime.globalVars.game_finished,
     "collected": globalThis.savegame.collected
     };
    

    Even big games don't need to save all that much. Think for example Super Mario Odyssey. It's probably saving coins, progression, unlocks, moons, where mario should spawn and some whatever data here or there. I just cannot fathom a game that needs or benefits from this excessive type of saving system.

    I do certainly believe that a functional out-of-the-box savesystem would be very convenient and obvously should exist... but I really can't justify using it in the current state. And on the flipside, making a basic save system is really not that complicated.

  • I made heavy use of the No Save behavior to keep things simple, but I kept running into issues. A lot of them were fixed, but I keep finding more, either bugs, edge cases, or weird quirks.

    With a large and complex project, I have to ask - how do you know these are not logic issues with your project? It is difficult to rule that out, and I do think it's sometimes unfair to blame Construct for things you can't prove are definitely it's fault - especially when we frequently deal with bugs along the lines of "Construct is not handling XYZ properly!" which then turn out to be a problem with the user project or some other kind of misunderstanding instead. From my point of view the savegame feature is a heavily used feature that's been used by thousands of projects over many years, and there are currently very few known issues with it, which is usually good evidence it's actually pretty robust. One of the reasons we have our bug report guidelines is you need to follow that to prove it really is a problem with Construct, and not yet another case of a misunderstanding or project logic issue. I know that can be a pain, but it's generally the only way things ultimately get sorted out. And if you can't narrow it down, that can be a sign it's not actually a bug in Construct.

    If you make your own save system, it's also likely to run in to all sorts of bugs and issues that Construct's save system has already sorted. For example saving and loading the relationships between objects is particularly tricky. Take two instances A and B, with B being a child of A. If you load A and B doesn't exist yet, you can't set up the hierarchy; if you load B and A doesn't exist yet, you can't set up the hierarchy. You have to have a phased approach where you load A excluding hierarchy, load B excluding hierarchy, then afterwards when both exist, restore the hierarchy relationships. There are dozens of awkward cases like this through the savegame system. It's entirely possible that if you try to take this on yourself and re-do all the same logic Construct already handles in the savegame feature, you'll actually end up with a more buggy and inconsistent system - another reason it's better to focus on the built-in feature.

    Also, just to confirm: is the Drawing Canvas object not saved by the system?

    Drawing Canvases are saved, just not the pixel data inside them - so just the number of instances, their positions, sizes etc. Saving the pixel data inside them is actually quite a tricky feature to design. Should it save a smaller lossy image format, or store it uncompressed? If lossy, then savegames degrade the quality of images, which may end up looking bad. If uncompressed, a HD image is about 8 MB, which could be solely responsible for making a savegame go from say 10KB to 8 MB; if you have cloud saves suddenly that's a big, slow save using lots of storage quota. It'll get worse very quickly if you have more Drawing Canvases. Then in some cases if you do something like procedurally generate the pixel data, it may be entirely unnecessary anyway. Drawing Canvas has other features to save the pixel data, so the idea is if you want to do that, you can opt-in and save additional data alongside the savegame.

    Updating the game and loading a savestate from an old version sounds extremely iffy, especially if the loaded objects have changed for whatever reason

    Construct's savegame feature is actually specifically designed to handle this gracefully. Everything internally relies on fixed IDs (called SIDs - serialization IDs) that are robust even in the face of renaming or moving things. You can rename all your object types, layouts, families, instance variables, behaviors, etc. and savegame data will be restored exactly the same. Data for deleted objects is just ignored, and newly added objects since the last savegame are not restored as there isn't any data for them. So it should in general just work. I don't recall anyone having issues with updating a project using savegames in the past few years, so from my point of view that seems to all be working. It's also another thing you have to think about if you invent your own savegame system. Will it be robust if you rename an object or a variable? Are you tracking things with fixed IDs to make it robust? It seems to me another non-obvious thing that you might get wrong if you try to make your own savegame system, and then you've invented a worse system that you are more likely to break.

  • Ashley thank you for the detailed reply.

    Our custom save system has been working well for SkyRanch.Life, and we do deal with hierarchies, but not much that's made dynamically and attached on the fly. As we continue development, there's always a chance that something will stop working and become a headache. I love headaches.

    Our system is pretty tedious and annoying, I'd gladly swap to the native one. I will give it a try before an update and report my findings (maybe within a month or so). I expect the cross version save will not work but we'll see.

    I would be thrilled to be proven wrong so badly that we can actually swap to C3's Save and Load system. I will personally send a sorry & thank you letter to Scirra's office by snail mail.

    Drawing Canvas not saving pixel info

    Interesting idea about the drawing canvas, but shouldn't be a problem for the more complex Steam games. Numerous games on Steam already have huge save games. I know Baldur's Gate 3 can be up to 400mb per file and it works well. I still think you should give the user this ability to decide for themselves, with proper documentation.

    We are actually using Drawing Canvas both in Nightwalkers.io and SkyRanch.Life. Nightwalkers isn't planed for Steam for the next 2 years. We use it to save effects of zombies being dead in the single player dungeon we got (since Nightwalkers is multiplayer).

    I could not allow the player to save the game when they're there I guess, or use C3's Save/Load as well as another system to save Drawing Canvas info, maybe. I've not tried. Sounds pretty complicated.

  • That's a great point on giving the user the option, if I were doing a quick 4 month project and ran into the situation where I specifically want the canvases to be saved, I would probably not be able to bother making a system for that and a checkbox given by Construct would be neat, as you can control which canvases to destroy pre-save anyway.

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • The biggest problems are these:

    The manual says nothing about the limitations (drawing canvas not being saved, waits not being saved even though there's no networking done). So we're left to test and guess.

    and

    We need to strip down huge projects to repro a bug, fair, but we also pay for C3, so at this point we're doing QA, and we pay for it. which is not as fair as I'd expect.

    I know small time game devs that hired QA companies to deal with their bugs, surely Scirra can figure out something. Plenty of good bug hunters on C3's github issues.

    Do you know how much it took me take to strip down these to report some bugs? Days. And as yours, my time is also not free.

    If C3 would have some paid testers, they could be the ones to strip down the project on our behalf, the clients, in order to find bugs in C3.

    If C3 actually wants to fix bugs and not rely on the game developers to do half of the job.

    I was a tester for EA Games, it's not easy work.

  • If you make your own save system, it's also likely to run in to all sorts of bugs and issues that Construct's save system has already sorted. For example saving and loading the relationships between objects is particularly tricky. Take two instances A and B, with B being a child of A. If you load A and B doesn't exist yet, you can't set up the hierarchy; if you load B and A doesn't exist yet, you can't set up the hierarchy. You have to have a phased approach where you load A excluding hierarchy, load B excluding hierarchy, then afterwards when both exist, restore the hierarchy relationships.

    I agree that this is indeed a very tricky issue. But I feel like this is a rather rare, specific situation where you'd actually be required to save this in detail? Maybe I'm wrong? Extending this question to the others in the thread: Does your game need this, and why?

    Construct's savegame feature is actually specifically designed to handle this gracefully. Everything internally relies on fixed IDs (called SIDs - serialization IDs) that are robust even in the face of renaming or moving things.

    Interesting.

    If C3 would have some paid testers, they could be the ones to strip down the project on our behalf, the clients, in order to find bugs in C3.

    If you think stripping down a gigantic 7000 event project takes long... imagine doing that except you are entirely unfamiliar with the coding style and codebase. :D

  • All software, commercial and otherwise, has bugs. Even software with extremely rigorous testing procedures and thousands of professional testers still have bugs, and still need to accept bug reports from users to make sure every problem gets sorted. Just look at Windows, Chrome, Safari, etc. Accepting issue reports from users is industry standard practice - in fact not accepting issue reports from the public is a negative signal for software quality in my experience (see: graphics drivers).

    The problem we have with Construct is you can make very large and complex projects with it that are particularly time-consuming to investigate, and we're a small company with limited resources. In exceptional circumstances we will try and investigate user's full projects, but it is usually infeasible: there is just so much going on that there is no way to identify how a bug could possibly have happened. Reducing the project ourselves is possible, but extremely time consuming, and usually takes far more time consuming than the original developer doing it. I don't think people appreciate just how difficult it is to be handed a vast project that you didn't make yourself. You start not even knowing how to play the game in question. It is even worse if the issue only occurs on level 25 which takes an hour to reach and involves difficult gameplay challenges before the issue occurs. You can try and cheat, but often making changes to a project you didn't make just breaks it because you don't understand it; then sometimes the issue doesn't reproduce after you made a cheat anyway. You can try and strip it down but it can take hours of examining the project to even know where to start. Then like I say often the end result is that you ultimately discover a mistake in event 4520 action 15 that actually caused the problem. If it took a week to reach that point, it will have meant missing an entire beta release of other improvements that could have been made in that time. We could hire professional testers to take this on, but in my view that would prove a very expensive and time consuming thing to do that produces minimal results, making it hard to justify. So I'm afraid the status quo is probably the best we can do for now.

    FWIW, we are often in the same boat when we report bugs to other developers ourselves. For example if I report a bug in Chrome, they rarely accept a reproduction using the entire Construct engine for the same reasons - it's too time-consuming to investigate and too frequently does not actually come down to being an actual bug. It can take significant time to strip things down and that can be frustrating when you're busy, so I totally appreciate that. However it's basically how things get done in the software industry. A good bug report with a minimal repro can sometimes literally be fixed in 5 minutes. A whole project with a vague report of an occasional issue can take weeks of work and still come to no resolution, so it's not generally something that is done in the industry - although if anyone does it, it's trillion-dollar multinational corporations with tens of thousands of staff who can afford to have a member of staff work on a possibly fruitless investigation for a few weeks. So yeah, sorry, I know it's hard reporting bugs, but it's basically the only way to realistically get things done.

Jump to:
Active Users
There are 0 visitors browsing this topic (0 users and 0 guests)