Skip to content

IndexedDB ile oyun durumunu kaydetme ve yükleme ​

IndexedDB, tarayıcıda kalıcı oyun verileri için en iyi seçenektir. Büyük miktarda veriyi işler, çevrimdışı çalışır ve ana iş parçacığını engellemez.

1) Neden localStorage yerine IndexedDB? ​

ÖzelliklocalStorageIndexedDB
Depolama sınırı~5-10 MB50+ MB (çoğu zaman GB)
Veri türleriYalnızca dizelerNesneler, blob'lar, diziler
AsenkronHayır (engeller)Evet
İndeksli sorgularHayırEvet

Oyunlar için IndexedDB neredeyse her zaman doğru seçimdir.

2) Veritabanı açma ​

js
function openGameDB() {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open('MyGame', 1)
    
    request.onerror = () => reject(request.error)
    request.onsuccess = () => resolve(request.result)
    
    request.onupgradeneeded = (event) => {
      const db = event.target.result
      
      // Create stores
      if (!db.objectStoreNames.contains('saves')) {
        db.createObjectStore('saves', { keyPath: 'slot' })
      }
      if (!db.objectStoreNames.contains('settings')) {
        db.createObjectStore('settings', { keyPath: 'key' })
      }
    }
  })
}

3) Oyun durumunu kaydetme ​

js
async function saveGame(slot, gameState) {
  const db = await openGameDB()
  
  return new Promise((resolve, reject) => {
    const tx = db.transaction('saves', 'readwrite')
    const store = tx.objectStore('saves')
    
    const saveData = {
      slot,
      state: gameState,
      timestamp: Date.now(),
    }
    
    const request = store.put(saveData)
    request.onsuccess = () => resolve()
    request.onerror = () => reject(request.error)
  })
}

4) Oyun durumunu yükleme ​

js
async function loadGame(slot) {
  const db = await openGameDB()
  
  return new Promise((resolve, reject) => {
    const tx = db.transaction('saves', 'readonly')
    const store = tx.objectStore('saves')
    
    const request = store.get(slot)
    request.onsuccess = () => resolve(request.result?.state || null)
    request.onerror = () => reject(request.error)
  })
}

5) Tüm kayıtları listeleme ​

js
async function listSaves() {
  const db = await openGameDB()
  
  return new Promise((resolve, reject) => {
    const tx = db.transaction('saves', 'readonly')
    const store = tx.objectStore('saves')
    
    const request = store.getAll()
    request.onsuccess = () => resolve(request.result)
    request.onerror = () => reject(request.error)
  })
}

6) Bir kaydı silme ​

js
async function deleteSave(slot) {
  const db = await openGameDB()
  
  return new Promise((resolve, reject) => {
    const tx = db.transaction('saves', 'readwrite')
    const store = tx.objectStore('saves')
    
    const request = store.delete(slot)
    request.onsuccess = () => resolve()
    request.onerror = () => reject(request.error)
  })
}

7) Eksiksiz bir GameStorage sınıfı ​

js
class GameStorage {
  constructor(dbName = 'GameData', version = 1) {
    this.dbName = dbName
    this.version = version
    this.db = null
  }
  
  async init() {
    this.db = await this.openDB()
  }
  
  openDB() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, this.version)
      request.onerror = () => reject(request.error)
      request.onsuccess = () => resolve(request.result)
      request.onupgradeneeded = (e) => {
        const db = e.target.result
        if (!db.objectStoreNames.contains('saves')) {
          db.createObjectStore('saves', { keyPath: 'slot' })
        }
        if (!db.objectStoreNames.contains('settings')) {
          db.createObjectStore('settings', { keyPath: 'key' })
        }
        if (!db.objectStoreNames.contains('assets')) {
          db.createObjectStore('assets', { keyPath: 'url' })
        }
      }
    })
  }
  
  async save(slot, data) {
    const tx = this.db.transaction('saves', 'readwrite')
    tx.objectStore('saves').put({ slot, data, timestamp: Date.now() })
    return tx.complete
  }
  
  async load(slot) {
    const tx = this.db.transaction('saves', 'readonly')
    const result = await this.promisify(tx.objectStore('saves').get(slot))
    return result?.data || null
  }
  
  async setSetting(key, value) {
    const tx = this.db.transaction('settings', 'readwrite')
    tx.objectStore('settings').put({ key, value })
  }
  
  async getSetting(key, defaultValue = null) {
    const tx = this.db.transaction('settings', 'readonly')
    const result = await this.promisify(tx.objectStore('settings').get(key))
    return result?.value ?? defaultValue
  }
  
  promisify(request) {
    return new Promise((resolve, reject) => {
      request.onsuccess = () => resolve(request.result)
      request.onerror = () => reject(request.error)
    })
  }
}

8) İkili verileri (dokular, sesler) depolama ​

IndexedDB, Blob'ları ve ArrayBuffer'ları destekler:

js
async function cacheAsset(url, blob) {
  const tx = db.transaction('assets', 'readwrite')
  tx.objectStore('assets').put({ url, blob, cached: Date.now() })
}

async function getCachedAsset(url) {
  const tx = db.transaction('assets', 'readonly')
  const result = await promisify(tx.objectStore('assets').get(url))
  return result?.blob || null
}

9) Otomatik kayıt kalıbı ​

js
class AutoSave {
  constructor(storage, interval = 60000) {
    this.storage = storage
    this.interval = interval
    this.timer = null
    this.dirty = false
  }
  
  markDirty() {
    this.dirty = true
  }
  
  start(getState) {
    this.timer = setInterval(async () => {
      if (this.dirty) {
        await this.storage.save('autosave', getState())
        this.dirty = false
        console.log('Autosaved')
      }
    }, this.interval)
  }
  
  stop() {
    clearInterval(this.timer)
  }
}

10) Hata yönetimi ve geri dönüş seçenekleri ​

js
async function safeLoad(slot, defaultState) {
  try {
    const saved = await loadGame(slot)
    if (saved) {
      // Validate/migrate old saves if needed
      return migrateSave(saved)
    }
  } catch (err) {
    console.warn('Failed to load save:', err)
  }
  return defaultState
}

function migrateSave(save) {
  // Handle old save formats
  if (!save.version) {
    save.version = 1
    save.settings = save.settings || {}
  }
  return save
}

11) Kayıtları otomatik silinmeye karşı koruma ​

IndexedDB verilerinin kalıcı olacağı garanti edilmez. Bir kaynak varsayılan olarak "mümkün olan en iyi" depolamayı kullanır ve disk dolduğunda ya da Safari/WebKit'te kullanıcı sitenizle uzun süre etkileşim kurmadığında tarayıcı bu verileri silebilir. Oyun kayıtları ise tam olarak kaybetmek istemeyeceğiniz verilerdir.

Tarayıcının açık bir kullanıcı işlemi olmadan verilerinizi temizlememesi için kalıcı depolama isteyin:

js
async function makeStoragePersistent() {
  if (navigator.storage && navigator.storage.persist) {
    const persisted = await navigator.storage.persist()
    console.log(persisted ? 'Saves are protected from eviction' : 'Saves may be evicted under storage pressure')
    return persisted
  }
  return false
}

Tarayıcılar bu izni etkileşim sinyallerine (kullanıcının sitenizle ne kadar etkileşim kurduğu, sitenin PWA olarak yüklenip yüklenmediği) göre verir; bu nedenle isteğin her zaman başarılı olacağını varsaymayın. Büyük kayıtları veya önbelleğe alınmış varlıkları yazmadan önce ne kadar alanınız kaldığını da kontrol edebilirsiniz:

js
async function checkStorage() {
  if (navigator.storage && navigator.storage.estimate) {
    const { usage, quota } = await navigator.storage.estimate()
    console.log(`Using ${usage} of ${quota} bytes`)
  }
}

Her iki API de güvenli bir bağlam (HTTPS veya localhost) gerektirir.

İlgili içerikler ​

Harici Kaynaklar ​