Hi everyone, I’m really having trouble with this one. We just released our game on Android and IOS several users are reporting that their saved data has been cleared after opening and closing the game over 20 times in some cases. Even though the saving and loading is successful most of the time. I haven’t been able to repeat this issue and I can’t find any commonalities. Some players have never experienced it on the same version of the game after playing for 30 hours.
If anyone has any leads on this or hints on how I would track this down or repeat the issue I’m open to anything. This is really making our release tough, even though the rest of it is going well.
Any help is deeply appreciated.
Here is some of the code I am using for saving. Let me know if anything sticks out at you as being very wrong. Essentailly if the loading script fails in anyway to find the data the game will assume that this is a fresh install and clear all variables to get ready for a new game. So I’m sure whatever is going on is cuasing the save file that is saving correctly to either be missnamed, missplaced or corrupted so that it wont load. My guess is corrupted but again I can’t repeat this even if I force quit in very save heavy parts of the game. I’m at a total loss on what’s going on here. Someone suggested that it might be Android autobackup, but then the IOS auto backup would also need to be causing and issue. Would that be the sort of thing that could clear all of the data for my app or corrupt it?
Saving function
public void SaveGameStats()
{
string saveFileName = "GameStats.dat";
if (GameManager.instance.hardCoreMode)
{
saveFileName = "GameStatsH.dat";
}
BinaryFormatter bf = new BinaryFormatter();
FileStream file = null;
if (File.Exists(Path.Combine(Application.persistentDataPath, saveFileName)))
{
File.Delete(Path.Combine(Application.persistentDataPath, saveFileName));
}
savableGameStats.currentSuccesiveMission = currentSuccessiveMission;
TotalPlayTime += Time.realtimeSinceStartup - lastTimeSinceStartSeconds; // get the number of seconds the game has been running and convert it to a timespan
lastTimeSinceStartSeconds = Time.realtimeSinceStartup;
savableGameStats.enemStatListY.Clear(); // clear list to make sure its empty
for (int y = 0; y < 7; y++)
{ // add seven 0's to enem stat list y so that the enem stat list can use the keys as the x wihtout throwing an out of range error
savableGameStats.enemStatListY.Add(0);
}
for (int i = 0; i < enemyStatAddList.Count; i++)
{
savableGameStats.enemStatListY[i] = enemyStatAddList[i].y;
}
savableGameStats.hardCoreMode = hardCoreMode;
if (savableAccountStats.adFreeVersion == true)
{
savableAccountStats.adFreeVersion = true;
}
savableGameStats.sfxVolume = sfxVolume;
savableGameStats.musicVolume = musicVolume;
savableGameStats.HCPPassiveHeal = HCPPassiveHeal;
savableGameStats.HCPMissionReRolls = HCPMissionReRolls;
savableGameStats.missionReRolls = missionReRolls;
savableGameStats.HCPInjuryHeal = HCPInjuryHeal;
savableGameStats.BCPBuffMirror = BCPBuffMirror;
savableGameStats.BCPAbilityPower = BCPAbilityPower;
savableGameStats.BCPAbilityCooldown = BCPAbilityCooldown;
savableGameStats.HCPAbilityPower = HCPAbilityPower;
savableGameStats.unitDeadSaveChance = unitDeadSaveChance;
savableGameStats.itemDeadSaveChance = JunkyardSalvageFromUnits;
savableGameStats.currentChestType = currentChestType;
savableGameStats.currentChestMagicFind = currentChestMagicFind;
savableGameStats.currentChestItemNumber = currentChestItemNumber;
savableGameStats.currentArmy = currentArmy;
savableGameStats.arenaBet = CurrentArenaBet;
savableGameStats.arenaRarity = ArenaRunRarirty;
savableGameStats.isInArena = isInArena;
savableGameStats.energy = energy;
savableGameStats.bloodForgeStartingLevel = bloodForgeStartingLevel;
savableGameStats.savedBloodForgeItemId = currentBloodForgeItemId;
savableGameStats.bloodForgeSuccessPercentage = bloodForgeSuccessPercent;
savableGameStats.isInBloodForge = isInBloodForge;
savableGameStats.buildingBlueprintMissionNumber = buildingBluePrintMissionNumber;
savableGameStats.levelReward = levelReward;
savableGameStats.SCPResurection = SCPResurection;
savableGameStats.SCPExtraCp = SCPExtraCp;
savableGameStats.SCPITemPower = SCPITemPower;
savableGameStats.battleNumber = savedBattleNumber;
savableGameStats.lastTime = currentTime.ToBinary();
if (energyEmptyTime != null)
{
savableGameStats.energyEmptyTime = energyEmptyTime.ToBinary();
}
savableGameStats.totalPlayTime = TotalPlayTime;
savableGameStats.missionList = missionList;
savableGameStats.savedArmylist = currentArmyList;
savableGameStats.skillList = skillList;
savableGameStats.skillCostList = skillCostList;
savableGameStats.savedQuestList = currentQuestList;
savableGameStats.IAttackBuff = IAttackBuff;
savableGameStats.IdefenseBuff = IdefenseBuff;
savableGameStats.RattackSpeedBuff = RattackSpeedBuff;
savableGameStats.RaccuracyBuff = RaccuracyBuff;
savableGameStats.BhealthBuff = BhealthBuff;
savableGameStats.HdefenseBuff = HdefenseBuff;
savableGameStats.SHealthBuff = SHealthBuff;
savableGameStats.generalPortraitNum = generalPortaitNum;
savableGameStats.generalIsMale = generalIsMale;
savableGameStats.generalName = GeneralName;
savableGameStats.ICPAttackSpeed = ICPAttackSpeed;
savableGameStats.ICPBleed = ICPBleed;
savableGameStats.commerceYield = ICPGoldPerMission;
savableGameStats.HCPSalvage = HCPSalvage;
// savableGameStats.BCParmyCost = BCParmyCost;
savableGameStats.BCPBuildingSlots = BCPBuildingSlots;
savableGameStats.BCPBuildingCostReduction = BCPBuildingCostReduction;
savableGameStats.totalKills = totalKills;
savableGameStats.animatedTotalKills = animatedTotalKills;
savableGameStats.packItemChance = packItemChance;
// savableGameStats.armySizeList = currentArmySizeList;
savableGameStats.armySize = armySize;
savableGameStats.bloodLevel = bloodLevel;
savableGameStats.gold = gold;
savableGameStats.salvage = salvage;
savableGameStats.hasAllBuildings = hasAllBuildings;
savableGameStats.SCPMagicFind = SCPMagicFind;
savableGameStats.SCPLifeSteal = SCPLifeSteal;
savableGameStats.BattlesWon = battlesWon;
savableGameStats.ICp = ICp;
savableGameStats.totalCpEver = totalCpEver;
savableGameStats.RCp = RCp;
savableGameStats.RCPMaxCollectionSize = RCPMaxCollectionSize;
savableGameStats.BCp = BCp;
savableGameStats.HCp = HCp;
savableGameStats.SCp = SCp;
savableGameStats.Gp = Gp;
savableGameStats.packCost = packCost;
savableGameStats.idcount = idCount;
savableGameStats.firstgame = firstGameEver;
savableGameStats.fame = fame;
savableGameStats.maxAct = maxAct;
savableGameStats.fameTillNext = fameTillNextAct;
savableGameStats.campaignMission = currentCampaignMission;
savableGameStats.RCPCritCD = RCPCritCD;
savableGameStats.goToCustomizer = goToCustomizer;
savableGameStats.openUnitPanel = openUnitPanel;
savableGameStats.dragUnitsToArmy = dragUnitsToArmy;
savableGameStats.goToItems = goToItems;
savableGameStats.openItemsPanel = openItemsPanel;
savableGameStats.clickOpenUnit = clickOpenUnit;
savableGameStats.dragItemsToUnits = dragItemsToUnits;
savableGameStats.buildingsTip = buildingsTip;
savableGameStats.civScreenTutTip = civScreenTutTip;
savableGameStats.wordMapTutTip = wordMapTutTip;
savableGameStats.shopScreenTutTip = shopScreenTutTip;
savableGameStats.goToCpTutTip = goToCpTutTip;
savableGameStats.goToShopTutTip = goToShopTutTip;
savableGameStats.salvageShopTutTip = salvageShopTutTip;
savableGameStats.nextActTutTip = nextActTutTip;
savableGameStats.injuryTutTip = injuryTutTip;
savableGameStats.retireFarmerTutTip = retireFarmerTutTip;
file = File.Create(Path.Combine(Application.persistentDataPath, saveFileName));
bf.Serialize(file, savableGameStats);
file.Close();
SaveAccount(); // this is to make sure the account data is saved frequently
}
Loading Function
public void LoadGameStats()
{
string saveFileName = "GameStats.dat";
if(GameManager.instance.hardCoreMode)
{
saveFileName = "GameStatsH.dat";
}
if (File.Exists(Path.Combine(Application.persistentDataPath, saveFileName)))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Path.Combine(Application.persistentDataPath, saveFileName), FileMode.Open);
GameStats loadedStats = (GameStats)bf.Deserialize(file);
file.Close();
savableGameStats = loadedStats;
clearEnemStatList(); // clear list first to make sure we start clean
for (int i = 0; i < savableGameStats.enemStatListY.Count; i++)
{ // for all the values of the stats
addToEnemStatList(new Vector2(i + 1, savableGameStats.enemStatListY[i]));
}
if (savableGameStats.adFreeVersion == true)
{
adFreeVersion = true;
}
if (savableAccountStats.adFreeVersion == true) //this is a patch for people who bought the game before account stats exsisted
{
adFreeVersion = savableAccountStats.adFreeVersion;
}
hardCoreMode = savableGameStats.hardCoreMode;
energy = savableGameStats.energy;
sfxVolume = savableGameStats.sfxVolume;
musicVolume = savableGameStats.musicVolume;
musicSource.volume = musicVolume;
HCPPassiveHeal = savableGameStats.HCPPassiveHeal;
HCPMissionReRolls = savableGameStats.HCPMissionReRolls;
missionReRolls = savableGameStats.missionReRolls;
HCPInjuryHeal = savableGameStats.HCPInjuryHeal;
BCPAbilityPower = savableGameStats.BCPAbilityPower;
HCPAbilityPower = savableGameStats.HCPAbilityPower;
BCPAbilityCooldown = savableGameStats.BCPAbilityCooldown;
BCPBuffMirror = savableGameStats.BCPBuffMirror;
SCPResurection = savableGameStats.SCPResurection;
SCPExtraCp = savableGameStats.SCPExtraCp;
SCPITemPower = savableGameStats.SCPITemPower;
unitDeadSaveChance = savableGameStats.unitDeadSaveChance;
JunkyardSalvageFromUnits = savableGameStats.itemDeadSaveChance;
currentChestType = savableGameStats.currentChestType;
currentChestMagicFind = savableGameStats.currentChestMagicFind;
currentChestItemNumber = savableGameStats.currentChestItemNumber;
currentArmy = savableGameStats.currentArmy;
bloodForgeStartingLevel = savableGameStats.bloodForgeStartingLevel;
currentBloodForgeItemId = savableGameStats.savedBloodForgeItemId;
isInArena = savableGameStats.isInArena;
buildingBluePrintMissionNumber = savableGameStats.buildingBlueprintMissionNumber;
isInBloodForge = savableGameStats.isInBloodForge;
bloodForgeSuccessPercent = savableGameStats.bloodForgeSuccessPercentage;
CurrentArenaBet = savableGameStats.arenaBet;
ArenaRunRarirty = savableGameStats.arenaRarity;
currentSuccessiveMission = savableGameStats.currentSuccesiveMission;
// currentSuccessiveMission = null;
armySize = savableGameStats.armySize;
levelReward = savableGameStats.levelReward;
ICPGoldPerMission = savableGameStats.commerceYield;
HCPSalvage = savableGameStats.HCPSalvage;
SCPLifeSteal = savableGameStats.SCPLifeSteal;
SCPMagicFind = savableGameStats.SCPMagicFind;
ICPAttackSpeed = savableGameStats.ICPAttackSpeed;
ICPBleed = savableGameStats.ICPBleed;
battlesWon = savableGameStats.BattlesWon;
hasAllBuildings = savableGameStats.hasAllBuildings;
// BCParmyCost = savableGameStats.BCParmyCost;
totalKills = savableGameStats.totalKills;
animatedTotalKills = savableGameStats.animatedTotalKills;
BCPBuildingCostReduction = savableGameStats.BCPBuildingCostReduction;
BCPBuildingSlots = savableGameStats.BCPBuildingSlots;
packItemChance = savableGameStats.packItemChance;
RCPCritCD = savableGameStats.RCPCritCD;
RCPMaxCollectionSize = savableGameStats.RCPMaxCollectionSize;
// if (loadedStats.armySizeList != null) { // if we load a null list it will break the game and the list
// currentArmySizeList = savableGameStats.armySizeList;
// }
if (loadedStats.savedArmylist != null)
{
currentArmyList = savableGameStats.savedArmylist;
}
// if (loadedStats.armyTypes != null) { // if we load a null list it will break the game and the list
// armyTypes = savableGameStats.armyTypes;
// }
// if (loadedStats.armyNameList != null) { // if we load a null list it will break the game and the list
// armyNames = savableGameStats.armyNameList;
// }
if (loadedStats.skillList != null)
{ // if we load a null list it will break the game and the list
skillList = savableGameStats.skillList;
}
if (loadedStats.skillCostList != null)
{ // if we load a null list it will break the game and the list
skillCostList = savableGameStats.skillCostList;
}
else
{
for (int i = 0; i < 25; i++)
{
skillCostList.Add(0);
}
}
if (loadedStats.savedQuestList != null)
{ // if we load a null list it will break the game and the list
currentQuestList = savableGameStats.savedQuestList;
}
if (loadedStats.missionList != null)
{ // if we load a null list it will break the game and the list
missionList = savableGameStats.missionList;
}
else
{
missionList.MissionListStart();
}
savedBattleNumber = savableGameStats.battleNumber;
battleNumber = savableGameStats.battleNumber;
lastTime = currentTime;
TotalPlayTime = savableGameStats.totalPlayTime;
IAttackBuff = savableGameStats.IAttackBuff;
IdefenseBuff = savableGameStats.IdefenseBuff;
RattackSpeedBuff = savableGameStats.RattackSpeedBuff;
RaccuracyBuff = savableGameStats.RaccuracyBuff;
BhealthBuff = savableGameStats.BhealthBuff;
HdefenseBuff = savableGameStats.HdefenseBuff;
SHealthBuff = savableGameStats.SHealthBuff;
generalPortaitNum = savableGameStats.generalPortraitNum;
generalIsMale = savableGameStats.generalIsMale;
GeneralName = savableGameStats.generalName;
bloodLevel = savableGameStats.bloodLevel;
salvage = savableGameStats.salvage;
gold = savableGameStats.gold;
packCost = savableGameStats.packCost;
ICp = savableGameStats.ICp;
totalCpEver = savableGameStats.totalCpEver;
RCp = savableGameStats.RCp;
BCp = savableGameStats.BCp;
HCp = savableGameStats.HCp;
SCp = savableGameStats.SCp;
Gp = savableGameStats.Gp;
firstGameEver = savableGameStats.firstgame;
idCount = savableGameStats.idcount;
fame = savableGameStats.fame;
maxAct = savableGameStats.maxAct;
fameTillNextAct = savableGameStats.fameTillNext;
currentCampaignMission = savableGameStats.campaignMission;
goToCustomizer = savableGameStats.goToCustomizer;
openUnitPanel = savableGameStats.openUnitPanel;
dragUnitsToArmy = savableGameStats.dragUnitsToArmy;
goToItems = savableGameStats.goToItems;
openItemsPanel = savableGameStats.openItemsPanel;
clickOpenUnit = savableGameStats.clickOpenUnit;
dragItemsToUnits = savableGameStats.dragItemsToUnits;
civScreenTutTip = savableGameStats.civScreenTutTip;
wordMapTutTip = savableGameStats.wordMapTutTip;
shopScreenTutTip = savableGameStats.shopScreenTutTip;
goToCpTutTip = savableGameStats.goToCpTutTip;
goToShopTutTip = savableGameStats.goToShopTutTip;
salvageShopTutTip = savableGameStats.salvageShopTutTip;
buildingsTip = savableGameStats.buildingsTip;
nextActTutTip = savableGameStats.nextActTutTip;
injuryTutTip = savableGameStats.injuryTutTip;
retireFarmerTutTip = savableGameStats.retireFarmerTutTip;
if (savableGameStats.energyEmptyTime != 0) {
energyEmptyTime = DateTime.FromBinary(savableGameStats.energyEmptyTime);
}
}
}