How to save data with a webGL build?

Hey guys, I use serialization in my game for the windows build, and want to make a webGL build so I can host it on online websites, and serialization didnt work. I made everything playerPrefs next, which said that it will save in the internalDB in the browser, and it also didnt work for me. I did a windows build with playerprefs and it worked, so my code is fine, just the webgl version is giving me problems. Is there anything I am doing wrong? Do I have to use a database for this simple game? Does anyone else have experience doing this? Thanks!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System;

public class SaveLoadWebGL : MonoBehaviour
{

    public GameManager GM;
    public MegApartmentManager MM;
    public PromotionManager PM;
    public CashPerSec CPS;
    public REPurchases[] RE;
    public AchievementManager[] AM;
    public MailboxManager MaM;
    public RCSpending[] RCS;
    public UpgradeManager[] UaaG;
    public AchievementProgress AP;
    public MegApartmentTree[] MAT;
    public string[] testArray;
    public float cashWhileGone;
    public CashPerSec cps;
    public long ticksSinceClose;
    public long totalSeconds;
    public int mailboxTicks;
    public long test;
    public long test2;
    public bool a = false;
    public long b;
    public bool c = false;


    public void Save()
    {
        PlayerPrefs.SetInt("RELength", RE.Length);
        PlayerPrefs.SetInt("AMLength", AM.Length);
        PlayerPrefs.SetInt("UaaGLength", UaaG.Length);
        PlayerPrefs.SetInt("MATLength", MAT.Length);
        PlayerPrefs.SetInt("RCSLength", RCS.Length);

        for (int i = 0; i < RE.Length; i++)
        {
            PlayerPrefs.SetInt("REMultiplier" + i, RE[i].multiplier);
            PlayerPrefs.SetInt("RECount" + i, RE[i].count);
            PlayerPrefs.SetFloat("RECost" + i, RE[i].cost);

            if (RE[i].onoff)
                PlayerPrefs.SetInt("REOnOff" + i, 1);
            else
                PlayerPrefs.SetInt("REOnOff" + i, 0);
        }

        for (int i = 0; i < AM.Length; i++)
        {
            if (AM[i].achieved)
                PlayerPrefs.SetInt("AMAchieved" + i, 1);
            else
                PlayerPrefs.SetInt("AMAchieved" + i, 0);
        }

        for (int i = 0; i < UaaG.Length; i++)
        {
            if (UaaG[i].purchased)
                PlayerPrefs.SetInt("UaaGPurchased" + i, 1);
            else
                PlayerPrefs.SetInt("UaaGPurchased" + i, 0);
        }

        for (int i = 0; i < MAT.Length; i++)
        {
            if (MAT[i]._active)
                PlayerPrefs.SetInt("MATActive" + i, 1);
            else
                PlayerPrefs.SetInt("MATActive" + i, 0);
        }

        for (int i = 0; i < RCS.Length; i++)
        {
            string[] aa = new String[RCS.Length];

            aa[i] = RCS[i].timeLeft.Ticks.ToString();
            PlayerPrefs.SetString("RCSTimeLeft" + i, aa[i]);
        }

        PlayerPrefs.SetFloat("Cash", GM.cash);
        PlayerPrefs.SetFloat("SalaryClick", GM.salaryClick);
        PlayerPrefs.SetInt("Clicks", GM.clicks);
        PlayerPrefs.SetInt("promoLevel", PM.promoLevel);
        long a = DateTime.Now.Ticks;
        string c = a.ToString();
        PlayerPrefs.SetString("GameCloseTicks", c);
        PlayerPrefs.SetFloat("TotalMegApartments", MM.totalMegApartments);
        PlayerPrefs.SetFloat("CurrentMegApartments", MM.currMegApartments);
        PlayerPrefs.SetFloat("PercentIncrease", MM.percentIncrease);
        PlayerPrefs.SetFloat("TotalCash", GM.totalCash);
        PlayerPrefs.SetFloat("SessionCash", GM.sessionCash);
        PlayerPrefs.SetInt("RcToCollect", MaM.rcToCollect);
        PlayerPrefs.SetInt("MaxRcToCollect", MaM.maxRCToCollect);
        PlayerPrefs.SetInt("RentChecks", GM.rentChecks);
        string b = MaM.showTimeLeft.Ticks.ToString();
        PlayerPrefs.SetString("ShowTimeLeft", b);

        if (MaM.a == false)
            PlayerPrefs.SetInt("MaMA", 0);
        else
            PlayerPrefs.SetInt("MaMA", 1);

        if (MaM.b == false)
            PlayerPrefs.SetInt("MaMB", 0);
        else
            PlayerPrefs.SetInt("MaMB", 1);

        if (GM.a == false)
            PlayerPrefs.SetInt("GMa", 0);
        else
            PlayerPrefs.SetInt("GMa", 1);


        PlayerPrefs.SetInt("Achieved", AP.achieved);

        PlayerPrefs.Save();
    }

    public void Load()
    {
        if (PlayerPrefs.HasKey("RELength"))
        {
            for (int i = 0; i < PlayerPrefs.GetInt("RELength"); i++)
            {
                if (PlayerPrefs.HasKey("REMultiplier" + i))
                    RE[i].multiplier = PlayerPrefs.GetInt("REMultiplier" + i);
            }
        }

        if (PlayerPrefs.HasKey("RELength"))
        {
            for (int i = 0; i < PlayerPrefs.GetInt("RELength"); i++)
            {
                if (PlayerPrefs.HasKey("RECount" + i))
                    RE[i].count = PlayerPrefs.GetInt("RECount" + i);
            }
        }

        if (PlayerPrefs.HasKey("RELength"))
        {
            for (int i = 0; i < PlayerPrefs.GetInt("RELength"); i++)
            {
                if (PlayerPrefs.HasKey("RECost" + i))
                    RE[i].cost = PlayerPrefs.GetFloat("RECost" + i);
            }
        }

        if (PlayerPrefs.HasKey("RELength"))
        {
            for (int i = 0; i < PlayerPrefs.GetInt("RELength"); i++)
            {
                if (PlayerPrefs.HasKey("REOnOff" + i) && PlayerPrefs.GetInt("REOnOff" + i) == 1)
                    RE[i].onoff = true;
                else if (PlayerPrefs.HasKey("REOnOff" + i) && PlayerPrefs.GetInt("REOnOff" + i) == 0)
                    RE[i].onoff = false;
            }
        }

        if (PlayerPrefs.HasKey("AMLength") && PlayerPrefs.HasKey("AMAchieved"))
        {
            for (int i = 0; i < PlayerPrefs.GetInt("AMLength"); i++)
            {
                if (PlayerPrefs.HasKey("AMAchieved" + i))
                {
                    if (PlayerPrefs.GetInt("AMAchieved" + i) == 1)
                        AM[i].achieved = true;
                    else if (PlayerPrefs.GetInt("AMAchieved" + i) == 0)
                        AM[i].achieved = false;
                }
            }
        }

        if (PlayerPrefs.HasKey("UaaGLength") && PlayerPrefs.HasKey("UaaGPurchased"))
        {
            for (int i = 0; i < PlayerPrefs.GetInt("UaaGLength"); i++)
            {
                if (PlayerPrefs.HasKey("UaaGPurchased" + i))
                {
                    if (PlayerPrefs.GetInt("UaaGPurchased" + i) == 1)
                        UaaG[i].purchased = true;
                    else if (PlayerPrefs.GetInt("UaaGPurchased" + i) == 0)
                        UaaG[i].purchased = false;
                }
            }
        }

        if (PlayerPrefs.HasKey("MATLength") && PlayerPrefs.HasKey("MATActive"))
        {
            for (int i = 0; i < PlayerPrefs.GetInt("MATLength"); i++)
            {
                if (PlayerPrefs.HasKey("MATActive" + i))
                {
                    if (PlayerPrefs.GetInt("MATActive" + i) == 1)
                        MAT[i]._active = true;
                    else if (PlayerPrefs.GetInt("MATActive" + i) == 0)
                        MAT[i]._active = false;
                }
            }
        }


        string c = PlayerPrefs.GetString("GameCloseTicks");
        long a = (long)Convert.ToInt64(c);

        if (PlayerPrefs.HasKey("GameCloseTicks") && PlayerPrefs.HasKey("RcToCollect") && PlayerPrefs.HasKey("ShowTimeLeft"))
        {
            ticksSinceClose = DateTime.Now.Ticks - a;
            totalSeconds = ticksSinceClose / TimeSpan.TicksPerSecond;
            MaM.rcToCollect = PlayerPrefs.GetInt("RcToCollect");

            c = PlayerPrefs.GetString("ShowTimeLeft");
            a = (long)Convert.ToInt64(c);


            if (ticksSinceClose > a)
            {
                b = ticksSinceClose - a;
                b /= TimeSpan.TicksPerSecond;
                MaM.rcToCollect++;

                if (b < MaM.timeToTick)
                    test = MaM.timeToTick - b;
                else
                    test = b % MaM.timeToTick;
            }
            else if (ticksSinceClose < a)
            {
                b = ticksSinceClose;
                test = a - b;
                test /= TimeSpan.TicksPerSecond;
                b /= TimeSpan.TicksPerSecond;
            }
            else if (ticksSinceClose == a)
            {
                b = 0;
                test = MaM.timeToTick;
            }


            for (int i = 0; i < PlayerPrefs.GetInt("RCSLength"); i++)
            {
                long[] aa = new long[PlayerPrefs.GetInt("RCSLength")];

                aa[i] = (long)Convert.ToInt64(PlayerPrefs.GetString("RCSTimeLeft" + i));
                RCS[i].timeLeft = TimeSpan.FromTicks(aa[i]) - TimeSpan.FromTicks(ticksSinceClose);

                if (RCS[i].timeLeft <= TimeSpan.FromTicks(0))
                    RCS[i].timeLeft = TimeSpan.FromTicks(0);
            }
        }

        if (PlayerPrefs.HasKey("SalaryClick") && PlayerPrefs.HasKey("Clicks") && PlayerPrefs.HasKey("promoLevel"))
        {
            cashWhileGone = totalSeconds * cps.GetCashPerSec();


            GM.salaryClick = PlayerPrefs.GetFloat("SalaryClick");
            GM.clicks = PlayerPrefs.GetInt("Clicks");
            PM.promoLevel = PlayerPrefs.GetInt("promoLevel");
            MM.totalMegApartments = PlayerPrefs.GetFloat("TotalMegApartments");
            MM.currMegApartments = PlayerPrefs.GetFloat("CurrentMegApartments");
            MM.percentIncrease = PlayerPrefs.GetFloat("PercentIncrease");
            GM.totalCash = PlayerPrefs.GetFloat("TotalCash");
            GM.sessionCash = PlayerPrefs.GetFloat("SessionCash");
            GM.cash = PlayerPrefs.GetFloat("Cash");
            GM.cash += cashWhileGone;
            MaM.maxRCToCollect = PlayerPrefs.GetInt("MaxRcToCollect");
            GM.rentChecks = PlayerPrefs.GetInt("RentChecks");

            MaM.showTimeLeft = TimeSpan.FromSeconds(test);

            mailboxTicks = (int)(b / MaM.timeToTick);

            if (mailboxTicks + MaM.rcToCollect < MaM.maxRCToCollect)
                MaM.rcToCollect += mailboxTicks;
            else
                MaM.rcToCollect = MaM.maxRCToCollect;

            if (PlayerPrefs.GetInt("MaMA") == 1)
                MaM.a = true;
            else
                MaM.a = false;

            if (PlayerPrefs.GetInt("MaMB") == 1)
                MaM.b = true;
            else
                MaM.b = false;

            if (PlayerPrefs.GetInt("GMa") == 1)
                GM.a = true;
            else
                GM.a = false;

            AP.achieved = PlayerPrefs.GetInt("Achieved");
        }
    }
}

Depending on where your game is located online, playerprefs doesn’t work. For example, on Facebook or Kongregate due to the fact the game plays in an iFrame(so other sources will not work either). WebGL can not use system.IO either, so anything relying on it will not work either.

You will need to use a database or a free backend. I would suggest if it’s not a lot of data, look into PlayFab as it’s got a free tier that is pretty decent for small projects.

Thank you very much! I will look into this and see if I can figure it out haha.