Managing Large Save Files

How do professional games handle really large save files, like open world games with lots of player progress?

Professional games use several strategies to handle large amounts of save data efficiently:

Incremental Saving

Instead of saving everything at once, games often save different components independently:

#include <chrono>

using namespace std::chrono;
class SaveManager {
  steady_clock::time_point LastInventorySave;
  steady_clock::time_point LastQuestSave;

 public:
  void Update() {
    auto Now = steady_clock::now();

    // Save inventory every 5 minutes
    if (Now - LastInventorySave > minutes(5)) {
      SaveInventory();
      LastInventorySave = Now;
    }

    // Save quests every 10 minutes
    if (Now - LastQuestSave > minutes(10)) {
      SaveQuests();
      LastQuestSave = Now;
    }
  }

  void SaveInventory() {
    // Only save items that changed since last save
    for (const auto& Item : PlayerInventory) {
      if (Item.HasChanged) {
        WriteItemToFile(Item);
      }
    }
  }
};

Chunked World Data

Open world games typically divide the world into chunks and only save active areas:

struct WorldChunk {
  static constexpr int SIZE = 64;
  std::array<std::array<Tile, SIZE>, SIZE>
    Tiles;
  bool IsModified{false};
};

class World {
  std::vector<std::vector<WorldChunk>> Chunks;

  void SaveActiveChunks() {
    const int PlayerChunkX = PlayerPos.X /
      WorldChunk::SIZE;
    const int PlayerChunkY = PlayerPos.Y /
      WorldChunk::SIZE;

    // Save chunks near player
    for (int dx = -1; dx <= 1; ++dx) {
      for (int dy = -1; dy <= 1; ++dy) {
        SaveChunkIfModified(PlayerChunkX + dx,
                            PlayerChunkY + dy);
      }
    }
  }
};

Compression

Large save files are typically compressed before writing to disk:

#include <zlib.h>
#include <vector>

std::vector<char> CompressSaveData(
  const std::vector<char>& Data) {
  std::vector<char> CompressedData;
  z_stream Stream{};

  // Initialize zlib
  deflateInit(&Stream, Z_BEST_COMPRESSION);

  // Compress data in chunks
  const int CHUNK_SIZE = 16384;
  char OutBuffer[CHUNK_SIZE];

  // Compression code here...

  deflateEnd(&Stream);
  return CompressedData;
}

Reference Data

Instead of saving full copies of common objects, save references to shared data:

struct Item {
  int TemplateId; // Reference to item template
  int Count; // Instance-specific data
  
  // Don't save template data like name, description, icon...
};

This combination of techniques allows games to manage massive amounts of save data while maintaining reasonable file sizes and save/load times.

Working with Data

Learn techniques for managing game data, including save systems, configuration, and networked multiplayer.

Questions & Answers

Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.

Managing Save File Versions
Is it possible to update my game without breaking existing save files from older versions?
Preventing Save File Tampering
What happens if a player tries to cheat by editing the save file? How can we prevent that?
Saving in Multiplayer Games
For multiplayer games, how do we handle saving when multiple players can modify the same object?
Cross-Platform Save Files
In real games, how do developers manage compatibility between different platforms' save files?
Implementing Autosave
How do games typically handle automatic saving? Should we save after every player action?
Or Ask your Own Question
Purchase the course to ask your own questions