Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

My save system works great in the editor but not on Android.

Discussion in 'General Discussion' started by EvanZSV, Dec 31, 2021.

  1. EvanZSV

    EvanZSV

    Joined:
    Jan 15, 2016
    Posts:
    5
    Logcat is showing a bunch of errors that aren't appearing in the editor.

    Code (CSharp):
    1. 2021/12/30 22:46:39.546 27307 27325 Error Unity NullReferenceException: Object reference not set to an instance of an object
    2. 2021/12/30 22:46:39.546 27307 27325 Error Unity   at MasterState.SaveProgress () [0x0000a] in <446d3805426249d5b22795fc2f4a1874>:0
    3. 2021/12/30 22:46:39.546 27307 27325 Error Unity   at SendSave.Save () [0x0000b] in <446d3805426249d5b22795fc2f4a1874>:0
    4. 2021/12/30 22:46:39.546 27307 27325 Error Unity   at UnityEngine.Events.InvokableCall.Invoke () [0x00010] in <d6c6277d5e64405f90a8f3061de6e382>:0
    5. 2021/12/30 22:46:39.546 27307 27325 Error Unity   at UnityEngine.Events.UnityEvent.Invoke () [0x00022] in <d6c6277d5e64405f90a8f3061de6e382>:0
    6. 2021/12/30 22:46:39.546 27307 27325 Error Unity   at UnityEngine.UI.Button.Press () [0x0001c] in <76fb17eeca0a42b194bccafc8dbd920f>:0
    7. 2021/12/30 22:46:39.546 27307 27325 Error Unity   at UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) [0x00009] in <76fb17eeca0a42b194bccafc8dbd920f>:0
    8. 2021/12/30 22:46:39.546 27307 27325 Error Unity   at UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) [0x00007] in <76fb17eeca0a42b194bccafc8dbd920f>:0
    9. 2021/12/30 22:46:39.546 27307 27325 Error Unity   at UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.Ev
    10.  
    I'm really not sure what to make of it.

    The code I'm using:
    Code (CSharp):
    1. public class SaveAndLoad : MonoBehaviour
    2. {
    3.     static public SaveAndLoad instance;
    4.     string filePath;
    5.  
    6.     private void Awake()
    7.     {
    8.         filePath = Application.persistentDataPath + "/save.bin";
    9.        
    10.         if (instance == null)
    11.         {
    12.             instance = this;
    13.         }
    14.         else
    15.         {
    16.             Destroy(gameObject);
    17.         }
    18.     }
    19.     public void SaveGame(GameData saveData)
    20.     {
    21.         FileStream dataStream = new FileStream(filePath, FileMode.Create);
    22.  
    23.         BinaryFormatter converter = new BinaryFormatter();
    24.         converter.Serialize(dataStream, saveData);
    25.  
    26.         dataStream.Close();
    27.     }
    28.     public GameData LoadGame()
    29.     {
    30.         if (File.Exists(filePath))
    31.         {          
    32.             FileStream dataStream = new FileStream(filePath, FileMode.Open);
    33.  
    34.             BinaryFormatter converter = new BinaryFormatter();
    35.             GameData saveData = converter.Deserialize(dataStream) as GameData;
    36.  
    37.             dataStream.Close();
    38.             return saveData;
    39.         }
    40.         else
    41.         {          
    42.             Debug.LogError("Save file not found in " + filePath);
    43.             return null;
    44.         }
    45.     }
    46. }
    This is all currently just hooked up to some GUI buttons. I've been googling for hours now but haven't been able to find anything that helps. The "NullReferenceException" is really throwing me off as that certainly isn't happening in the editor.
     
  2. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,327
    Your snippet does not contain the part that crashes your build.

    You need to look into MasterState.SaveProgress.
     
    Last edited: Dec 31, 2021
    TonyLi likes this.
  3. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,533
    Also, don't use BinaryFormatter. From Microsoft's BinaryFormatter Security Guide page:

     
  4. EvanZSV

    EvanZSV

    Joined:
    Jan 15, 2016
    Posts:
    5
    Code (CSharp):
    1. public class MasterState : MonoBehaviour
    2. {
    3.     GameData saveData = new GameData();
    4.     public ResourceManager resourceManager;
    5.     public SaveAndLoad saveAndLoad;
    6.     public int minedGold;
    7.     public int pannedGold;
    8.     public int food;
    9.     public int wood;
    10.     public int worms;
    11.  
    12.     private static MasterState instance;
    13.     private void Awake()
    14.     {
    15.         if (instance == null)
    16.         {
    17.             instance = this;
    18.             DontDestroyOnLoad(this.gameObject);
    19.             return;
    20.         }
    21.         Destroy(this.gameObject);
    22.     }
    23.     private void Start()
    24.     {
    25.         resourceManager = FindObjectOfType<ResourceManager>();
    26.         saveAndLoad = FindObjectOfType<SaveAndLoad>();
    27.     }
    28.  
    29.     public void SaveProgress()
    30.     {
    31.         Debug.Log("Saving Progress");
    32.         saveData.minedGold = resourceManager.minedGold;
    33.         saveData.pannedGold = resourceManager.pannedGold;
    34.         saveData.food = resourceManager.food;
    35.         saveData.wood = resourceManager.wood;
    36.         saveData.worms = resourceManager.worms;
    37.         SaveAndLoad.instance.SaveGame(saveData);
    38.     }
    39.     public void LoadProgress()
    40.     {
    41.         Debug.Log("Loading Progress");
    42.         saveData = SaveAndLoad.instance.LoadGame();
    43.         resourceManager.minedGold = saveData.minedGold;
    44.         resourceManager.pannedGold = saveData.pannedGold;
    45.         resourceManager.food = saveData.food;
    46.         resourceManager.wood = saveData.wood;
    47.         resourceManager.worms = saveData.worms;      
    48.     }  
    49.     public void ResetGame()
    50.     {
    51.         Debug.Log("Game is reset");
    52.         saveData.minedGold = 0;
    53.         saveData.pannedGold = 0;
    54.         saveData.food = 0;
    55.         saveData.wood = 0;
    56.         saveData.worms = 0;
    57.         SaveAndLoad.instance.SaveGame(saveData);
    58.     }    
    59.    
    60. }
    This is all of MasterState.
     
  5. EvanZSV

    EvanZSV

    Joined:
    Jan 15, 2016
    Posts:
    5
    What would be a better, secure, way of saving?
     
  6. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,533
    It really depends on your use case. But you could serialize to JSON and then encrypt it.
     
    MadeFromPolygons likes this.
  7. neginfinity

    neginfinity

    Joined:
    Jan 27, 2013
    Posts:
    13,327
    Two possible explanations:
    resourceManager is null.
    SaveAndLoad.instance is null.
     
    xVergilx likes this.
  8. EvanZSV

    EvanZSV

    Joined:
    Jan 15, 2016
    Posts:
    5
    Oh man...This is what I get for being up too late troubleshooting. In the editor, I'm just starting off in the game scene but the build starts at the main menu. Sure enough, when I start in the main menu it throws the same error in the editor.
     
    MadeFromPolygons and neginfinity like this.
  9. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,584
    Can Unity execute malwere injected to save file? That providing game allow for sharing saves with other players. Otherwise, why does it even matter for game saves?
     
    xVergilx likes this.
  10. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,930
    Because the BinaryFormatter is blindly deserialize the data, including system classes (the type of the deserialized data is coming from the data-stream itself and there is no whitelist/blacklist). Unless you sanitize the incoming data beforehand the modified data can be used as remote code execution attack. Now, if users _never_ share save files (including sending over the internet, there goes the cloud save), the risk is low, but I think it isn't worth it. It's easy to choose another serializer or simply writing your own save system writing out the data in a stream manually.
     
    TonyLi, Antypodish and neginfinity like this.
  11. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,294
    That's some next level paranoia right here.
    BinaryFormatter is great for performance. JSON - not so much.
    Using like Protobuf is nearly unmaintanable (in a medium to large project).
    BSON? Not worth the sacrifice, and it has its own cons.

    I'd take speed and maintainability over safety in this case.

    If device local storage is compromised, then its probably too late to blame the save files.

    It could be an issue, if its used as a binary serializer stream over network.
    But then again, you'd use either some kind of network lib, or write your own in this case.
    With precise data layout, and not BinaryFormatter.
     
    Last edited: Jan 2, 2022
  12. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,930
    Well, if I ever make it to an extent where multiple thousands play my game at once, I don't want to end up in the press as "the guy, who helped the latest botnet built".
    I agree, but it's not the point, I do not recommend JSON anything other than debugging. Never did.
    ROFL. I'm maintaining entire enterprise software applications built upon Protobuf, this is nonsense.
    I wouldn't. For example (if you don't want to write your own):

    https://github.com/TeamSirenix/odin-serializer
    Free, fresh, crunchy, from reputable source and probably safe (I'm not paranoid enough to put it through serious tests, that's why the "probably").
    Or if your users start to exchange save files and one of them is in the funny mood...
     
    angrypenguin and xVergilx like this.
  13. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,878

    What? You do realise that it is universally accepted as a standard of c# now not to use BinaryFormatter right? The details TonyLi linked are directly from msdn itself :)

    If the actual docs for BinaryFormatter literally say "never use this" then you really have to ask yourself why you are still using it and if its a sensible fight to keep on fighting ....

    BinaryFormatter will be deprecated and made obeselete, use something modern and safe.
     
    angrypenguin likes this.
  14. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,294
    Thing is, Android users almost never share local save data.
    Heck, most users don't even know how to open local storage.

    I'd say its still okay to use BF as is now. Its not a doomsday, or apocalypse.
    There's dosen ways to hack into someone elses data without relying on somebody manually replacing save files for the game.

    Especially on mobile, where applications run in semi-sandbox environment and may not even have proper "malicious" permissions. Angle of an attack and probablity if it happening in such conditions are close to zero.

    Then there's legacy projects. Good luck trying to persuade clients they need the change.

    As for the alternatives:
    JSON is performance taxing, no go.
    Protobuf requires manual data layout, which takes extra dev time to maintain.

    I haven't looked into Odin frankly. If it is as good as it looks:
    - Doesn't require extra LoC;
    - Have same or less GC / performance impact;
    And most importantly:
    - supports version control and class / data layout changes out of the box;

    then its probably a valid candidate to switch to for future projects.
     
    Last edited: Jan 3, 2022
    Lurking-Ninja likes this.
  15. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,516
    But it definitely is opening yourself to multiple potential threat vectors which can easily be protected using other approaches.

    This isn't relevant, though. Defensive design isn't about typical users, it's about the tiny percentage of people who crack things for fun or profit, in case you have the misfortune of one picking on you. And as @Lurking-Ninja alludes to, the chances of that increase as you get more successful.

    It's pretty standard for mobile games to sync to a server, though. If that server stores save data in unsanitised binary then it's a prime target for an injection attack which could, for example, result in your players' devices silently being co-opted into someone's botnet.

    That isn't "paranoia", by the way. It's just a numbers game. Crackers don't fill out their networks by subscribing to Amazon ECS. They actively scan for vulnerable online systems, either to exploit directly or to use in exploiting others.

    - - -

    For what it's worth, here's the kind of thing which happens when people decide that security doesn't matter for their game / toy / app: Data ... leaked and ransomed, exposing kids' voice messages. That article alone covers three different cases where basic security just wasn't used including one, separate to the headline case, which allowed a toy to "be remotely turned into a spy device".
     
    Last edited: Jan 4, 2022
  16. Neonlyte

    Neonlyte

    Joined:
    Oct 17, 2013
    Posts:
    505
    Uh, JSON is not the best performing serialization method out there, but surely for very infrequent operations like saving a game state, it is more than enough.
     
  17. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,533
    It also makes debugging save issues much easier since it's more readable than a binary stream.
     
  18. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,930
    And then people are wondering why their games a stuttering under the big pile of garbage they are creating... no, JSON parsing and creating in C# is bad. Very bad.
    This is why we have debug builds and release builds. The healthy way is outputting JSON in the editor and in debug builds and outputting binary in release.
    Or just write a short helper class to show the content of the binary save. It's not like you read save files day and night and writing one is how many, 10 lines of code?
    Better than completely unnecessarily put more work on the user's machine just because we became this lazy.
     
  19. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,516
    If it's causing stuttering then it's not "very infrequent".
     
    Neonlyte likes this.