Oyuncu verilerini ve satın alma sistemlerini uygula

*Bu içerik, yapay zekâ (beta) kullanılarak çevrildi ve hatalar içerebilir. Sayfayı İngilizce görüntülemek için buraya tıkla.

Arka Plan

Roblox, veri depolarıyla etkileşim kurmak için bir dizi API sağlar DataStoreService .Bu API'lerin en yaygın kullanım durumu, oyuncu verilerini kaydetmek, yüklemek ve yeniden yapmak için.Yani, oyuncunun ilerlemesi, satın alımları ve bireysel oyun oturumları arasında süren diğer oturum özellikleri ile ilgili veriler.

Roblox'taki çoğu deneyim, bir oyuncu veri sisteminin bir şekilde uygulanması için bu API'leri kullanır.Bu uygulamalar yaklaşımlarında farklılık gösterir, ancak genellikle aynı sorunları çözmeye çalışır.

Sıradan sorunlar

Aşağıda, oyuncu veri sistemlerinin çözmeye çalıştığı en yaygın sorunlardan bazıları bulunmaktadır:

  • Bellek Erişiminde: DataStoreService istekler asenkron olarak çalışan ve oran sınırlarına tabi olan web istekleri yapar.Bu, oturumun başında yapılacak ilk yükleme için uygundur, ancak oynanışnormal oynama kursu sırasında yüksek frekanslı okuma ve yazma operasyonları için değildir.Çoğu geliştiricinin oyuncu veri sistemleri, bu verileri Roblox sunucusunda bellekte saklar ve DataStoreService isteklerini aşağıdaki senaryolara sınırlar:

    • Bir oturumun başlangıcındaki ilk okuma
    • Oturumun sonunda son yazma
    • Sonraki yazma başarısız olduğu senaryoyu azaltmak için aralıklarla düzenli yazma yapılır Periodic writes at an interval to mitigate the scenario where the final write fails
    • Satın satın alişlemi sırasında verilerin kaydedilmesini sağlamak için yazılır
  • Verimli Depolama: Bir oyuncunun tüm oturum verilerini tek bir tabloda depolamak, çok sayıda değeri atomik olarak güncellemene ve daha az istekte aynı miktarda veriyi ele almana izin verir.Ayrıca aralarında değer uyuşmazlığı riskini ortadan kaldırır ve geri gitmeyi daha kolaylaştırır.

    Bazı geliştiriciler ayrıca büyük veri yapılarını sıkıştırmak için özel seriye dönüştürme uygular (genellikle oyun içi kullanıcı tarafından üretilen içeriği saklamak için).

  • Replikasyon: Müşterinin bir oyuncunun verilerine düzenli erişimi gerekir (örneğin, arayüzü güncellemek için).Oyuncu verilerini istemciye yeniden yazma genel bir yaklaşım, verilerin her bir bileşeni için özel yenleme sistemleri oluşturma zorunluluğu olmadan bu bilgileri iletebilmenizi sağlar.Geliştiriciler genellikle seçeneğin, müşteriye neyin ve neyin yinelenmediği konusunda seçici olmasını istiyor.

  • Hata Eleme: Veri depolarına erişilemediğinde, çoğu çözüm yeniden deneme mekanizmasını ve "Varsayılan" verilere geri dönmeyi uygulayacaktır.Geri dönüş veriye 'gerçek' verilerin üzerine yazılmayacağından ve bunun oyuncuya uygun bir şekilde iletileceğinden özel bakıma ihtiyaç duyulur.

  • Yeniden denemeler: Veri depolarına erişilemediğinde, çoğu çözüm bir yeniden deneme mekanizması ve varsayılan verilere bir geri dönüş mekanizması uygular.Geri dönüş veriye "gerçek" verilerin üzerine yazılmadığından emin olmak için özel bir dikkat gösterin ve durumu oyuncuya uygun bir şekilde iletin.

  • Oturum Kilitleme: Tek bir oyuncunun verileri yüklenir ve çok sayıda sunucuda bellekte tutulursa, bir sunucunun eski bilgileri kaydettiği sorunlar ortaya çıkabilir.Bu, veri kaybına ve yaygın öğe duplikasyon açıklarına yol açabilir.

  • Atomik Satın Alma İşleme: Eşyaların kaybolmasını veya birden fazla kez verilmesini engellemek için satın alma işlemlerini atomik olarak doğrula, ödül ve kaydet.

Örnek kod

Roblox, oyuncu veri sistemlerinin tasarımına ve oluşturulmasına yardımcı olmak için referans kodu sağlar.Bu sayfanın geri kalanı arka planı, uygulama ayrıntılarını ve genel uyarıları inceler.


Modeli Studio'ya aktardıktan sonra aşağıdaki klasör yapısını görmelisiniz:

Explorer window showing the purchasing system model.

Architecture Yapısı

Bu yüksek düzey diyagram, örnekteki ana sistemleri ve deneyimin geri kalanındaki kodla nasıl bağlandıklarını gösterir.

An architecture diagram for the code sample.

Yeniden denemeler

Sınıf: DataStoreWrapper

Arka Plan

As DataStoreService kaputun altında web istekleri yaparken, isteklerinin başarılı olacağı garanti edilmez.Bu gerçekleştiğinde, DataStore yöntemleri hata atar ve bunlarla başa çıkmanıza izin verir.

Böyle bir veri depolama hatasını ele almaya çalışırsanız ortak bir "gotcha" oluşabilir:


local function retrySetAsync(dataStore, key, value)
for _ = 1, MAX_ATTEMPTS do
local success, result = pcall(dataStore.SetAsync, dataStore, key, value)
if success then
break
end
task.wait(TIME_BETWEEN_ATTEMPTS)
end
end

Bu, genel bir işlev için mükemmel bir yeniden deneme mekanizması olmasına rağmen, DataStoreService istekleri için uygun değildir, çünkü isteklerin yapılacağı sırayı garanti etmiyor.İsteklerin sırasını korumak, DataStoreService istekleri devletle etkileşime girdiğinden önemlidir çünkü devletle etkileşime girerler.Aşağıdaki senaryoyu düşünün:

  1. Talebi A, anahtar değerini K 1'e ayarlamak için yapılır.
  2. İstek başarısız olur, bu nedenle 2 saniye içinde yeniden denemek planlanır.
  3. Yeniden deneme gerçekleşmeden önce, istek B değerini K 2 olarak ayarlar, ancak istek A'nın yeniden denemesi bu değeri hemen üzerine yazar ve K 1 olarak ayarlar.

Yine de UpdateAsync anahtarın değerinin en son sürümünde çalışsa da, UpdateAsync istekleri hala geçersiz geçici durumlardan kaçınmak için işlenmelidir (örneğin, bir satın alma işlemi bir para eklenmeden önce para çıkarır, sonuç olarak negatif para alınır).

Oyuncu veri sistemimiz, garantili olarak anahtara göre işlenecek yeniden denemeler sağlayan yeni bir sınıf kullanır, DataStoreWrapper , ki bunlar anahtara göre sıraya konur.

Yaklaşım

An process diagram illustrating the retry system

DataStoreWrapper``Class.GlobalDataStore|DataStore yöntemlerine karşılık gelen yöntemler sağlar: DataStore:GetAsync() , DataStore:SetAsync() , DataStore:UpdateAsync() ve DataStore:RemoveAsync() .

Bu yöntemler, çağrıldığında:

  1. Talebi bir sıraya ekleyin.Her anahtarın kendi sırası ve sırayla isteklerin işlendiği kendi sırası vardır.Talep eden işlem, istek tamamlanana kadar verir.

    Bu işlevsellik ThreadQueue sınıfına dayanmaktadır, ki bu bir rutin tabanlı görev planlayıcısı ve oran sınırlayıcıdır.Bir vaat dönüş yerine, ThreadQueue işlem tamamlanana kadar mevcut işletmeyi verir ve başarısız olursa bir hata oluşturur.Bu, idiomatik asenkron Luau modellerine daha uygun.

  2. Bir istek başarısız olursa, ayarlanabilir bir üstel geri çekme ile yeniden deneye alır.Bu yeniden denemeler, ThreadQueue adresine gönderilen geri çağrının bir parçasıdır, bu nedenle bu anahtar için sırada bir sonraki istek başlamadan önce bitirilmesi garanti edilir.

  3. Bir istek tamamlandığında, istek yöntemi success, result modeli ile döner

DataStoreWrapper Ayrıca, belirli bir anahtar için sıra uzunluğunu almak ve eski istekleri temizlemek için yöntemleri de açar.Son seçenek, sunucu kapatıldığında ve en yeni istekler işlenemediğinde özellikle yararlıdır. senaryolarda.

Uyarılar

DataStoreWrapper aşırı senaryoların dışında, her veri depolama isteğinin (başarılı veya başarısız olarak) tamamlanmasına izin verilmelidir, ancak daha yeni bir istek onu gereksiz kılarsa.Yeni bir istek oluştuğunda, eski istekler sıraydan kaldırılmaz, ancak yeni istek başlamadan önce bitirilmesine izin verilir.Bunun nedeni, oyuncu verileri için özel bir araç değil, genel bir veri depolama aracı olarak bu modülün uygulanabilirliğine dayanmaktadır ve şöyledir:

  1. Bir istek kuyruktan kaldırılabilir olup olmadığına karar vermek zordur. Aşağıdaki sırayı düşünün:

    Value=0, SetAsync(1), GetAsync(), SetAsync(2)

    Beklenen davranış, GetAsync() 'nin 1 geri döneceğidir, ancak en yeni olanı tarafından geçersiz kılındığı için sıraydan çıkarılan SetAsync() isteği nedeniyle geri dönecektir, 0 .

    Mantıksal ilerleme, yeni bir yazma isteği eklenirken sadece en son okuma talepkadar eski istekleri kısaltmak olduğudur.UpdateAsync() , şu ana kadar en yaygın işlem (ve bu sistem tarafından kullanılan tek işlem), her ikisini de okuyabilir ve yazabilir, bu yüzden bu tasarım içinde bu ekstra karmaşıklık eklemeksizin uzlaştırmak zor olacaktır.

    DataStoreWrapper bir UpdateAsync() isteğinin okunmasına veya yazılmasına izin verilip verilmediğini belirtmenizi gerektirebilir, ancak bu, oturum kilitleme mekanizması nedeniyle zaman öncesinde belirlenemeyecektir (daha sonra daha ayrıntılı olarak ele alınacak).

  2. Sıradan kaldırıldıktan sonra, bunun nasıl ele alınması gerektiğine dair anlaşılır bir kurala karar vermek zordur nasıl.Bir DataStoreWrapper isteği yapıldığında, mevcut işletim işi bitene kadar verilir.Eski istekleri sıradan çıkardıysak, false, "Removed from queue" geri dönüp dönmeyeceğimize veya aktif iş parçasını asla geri bırakıp atmayacağımıza karar vermeliyiz.Her iki yaklaşım da kendi dezavantajlarıyla gelir ve tüketiciye ekstra karmaşıklık yükler.

Sonuçta, görüşümüz basit yaklaşımın (her talepişleme) burada tercih edilir olması ve oturum kilitleme gibi karmaşık sorunlara yaklaşırken daha açık bir çevre oluşturmasıdır.Buna istisna, sırayı temizlemenin tüm kullanıcıların verilerini zamanında kurtarmak için gerekli hale geldiği DataModel:BindToClose() sırasında, değer bireysel işlev çağrılarının geri dönüşünün artık devam eden bir endişe olmadığıdır.Bunu hesaba katmak için, bir skipAllQueuesToLastEnqueued yöntemi sunuyoruz.Daha fazla kontekst için, Oyuncu Verileri görün.

Oturum kilitleme

Sınıf: SessionLockedDataStoreWrapper

Arka Plan

Oyuncu verileri sunucudaki belleğe depolanır ve gerektiğinde yalnızca okunur ve temel veri depolarına yazılır.Hafızada oynatıcı verilerini anında okuyabilir ve güncelleyebilirsiniz, web isteklerine ihtiyaç duymadan ve DataStoreService sınırlarını aşmaktan kaçının.

Bu model amaçlandığı gibi çalışması için, aynı anda birden fazla sunucunun bir oyuncunun verilerini belleğe yükleyebilmesi gereklidir DataStore .

Örneğin, eğer sunucu A bir oyuncunun verilerini yüklerse, sunucu B bu verileri bir sonraki kaydetmede kilidini açana kadar yükleyemez.Kilitleme mekanizması olmadan, sunucu B, sunucu A'nın hafızada bulunan daha yeni sürümü kaydetme şansı olmadan önce veri depolamasından eski oyuncu verilerini yükleyebilir.Sonra, sunucu A, sunucu B'nin eski verileri yükledikten sonra yeni verilerini kaydederse, sunucu B bir sonraki kaydı sırasında bu yeni veriyi üzerine yazacaktır.

Roblox yalnızca bir kullanıcının aynı anda bir sunucuya bağlanmasına izin verdiğine rağmen, bir oturumdaki verilerin her seferinde bir sonraki oturum başlamadan önce kaydedileceğini varsayamazsınız.Bir oyuncu sunucu A'dan ayrıldığında meydana gelebilecek aşağıdaki senaryoları düşünün:

  1. Sunucu A, verilerini kaydetmek için bir DataStore isteği yapar, ancak istek başarısız olur ve başarılı bir şekilde tamamlanması için birkaç yeniden denemeye ihtiyaç duyar.Yeniden deneme sırasında, oyuncu B sunucusuna katılır.
  2. Sunucu A aynı anahtara çok fazla UpdateAsync() çağrı yapar ve sınırlandırılır.Son kaydetme isteği bir sıraya yerleştirilir.İstek sıraya alındığında, oyuncu B sunucusuna katılır.
  3. Sunucu A'da, PlayerRemoving etkinliğine bağlı bazı kodlar oyuncunun verileri kaydedilmeden önce üretilir.Bu işlem tamamlanmadan önce, oyuncu B sunucusuna katılır.
  4. Sunucu A'nın performansı, oyuncu sunucuya B'ye katılana kadar sonraki bir tarihe kadar gecikmeli kaydetmeye düştü.

Bu senaryolar nadir olmalıdır, ancak meydana gelir, özellikle bir oyuncu bir sunucudan kopar ve diğerine hızlı bir şekilde bağlanır (örneğin, teleport ederken).Bazı kötü niyetli kullanıcılar bu davranışı kötüye kullanarak eylemleri bitirmek için devam etmeden eylemleri tamamlamaya çalışabilir.Bu, oyuncuların takas yapmasına izin veren oyunlarda özellikle etkili olabilir ve eşyaların kopyalanmasına yol açan yaygın bir kaynaktır.

Oturum kilitleme, bir oyuncunun DataStore anahtarının ilk önce sunucu tarafından okunmasını sağlayarak, sunucu atomik olarak aynı UpdateAsync() çağrı içinde anahtarın metadatosuna kilit yazar.Bu kilit değeri, başka bir sunucu anahtarı okumaya veya yazmaya çalıştığında mevcutsa, sunucu devam etmez.

Yaklaşım

An process diagram illustrating the session locking system

SessionLockedDataStoreWrapper , DataStoreWrapper sınıfının etrafındaki bir meta-paketleyicidir.DataStoreWrapper sıra oluşturma ve yeniden deneme işlevini sağlar, ki bu SessionLockedDataStoreWrapper oturum kilitleme ile desteklenir.

SessionLockedDataStoreWrapper her DataStore isteği geçer - GetAsync , SetAsync veya UpdateAsync olup olmadığına bakılmaksızın - UpdateAsync aracılığıyla.Bunun nedeni, UpdateAsync bir anahtarın atomik olarak hem okunup hem de yazılmasına izin vermesidir.Ayrıca dönüşüm geri çağrısında nil değerine dayalı yazmayı bırakmak da mümkündür.

Her bir isteğe aktarılan dönüşüm işlevi UpdateAsync için aşağıdaki işlemleri gerçekleştirir:

  1. Anahtarın erişime güvenli olduğunu doğrular ve eğer öyle değilse işlemi terk eder. "Erişim için güvenli" demek şunu ifade eder:

    • anahtarmeta veri nesnesi, kilit süresinin sona ermesinden daha az bir süre önce güncellenmeyen LockId değeri içermez.Bu, başka bir sunucu tarafından yerleştirilen bir kilidi saygı göstermeyi ve o kilidin sona erdiğinde göz ardı edilmesini sağlar.

    • Bu sunucu daha önce anahtarın metadına kendi LockId değerini yerleştirdiyse, bu değer hala anahtarın metadındadır.Bu, başka bir sunucunun bu sunucunun kilidini devraldığı (sona erdirilmesi veya zorla) ve daha sonra serbest bıraktığı durumu hesaba katmaktadır.Alternatif olarak formüle edilmiş olsa da, LockId eğer nil ise, başka bir sunucu hala anahtarı killettiğiniz süreden beri bir kilidi değiştirip kaldırabilir.

  2. Class.GlobalDataStore:UpdateAsync()|UpdateAsync``Class.GlobalDataStore|DataStore operasyonunu yerine getirir SessionLockedDataStoreWrapper , tüketici tarafından talep edilen. Örneğin, GetAsync()``function(value) return value end 'e çevirir.

  3. İsteğe aktarılan değerlere bağlı olarak, UpdateAsync talepanahtarını kilitler veya kilidini açar:

    1. Anahtar kilitleniyorsa, anahtarın meta verilerini bir GUID'e ayarlar.Bu GUID, anahtarın bir sonraki kez erişmesi durumunda sunucuda bellekte saklanır, böylece anahtar doğrulanabilir.Sunucunun zaten bu anahtar üzerinde kilidi varsa, değişiklik yapmaz.Ayrıca, anahtarın kilit süresi içinde yeniden erişmediğinizde uyarılmanız için bir görev planlar ve kilidin sona erme süresi içinde kilitlenmesini sağlar.

    2. Anahtarın kilidi açılacaksa, UpdateAsync anahtarın meta verilerindeki LockId kaldırır.

Özel bir yeniden deneme işlemi yöneticisi, oturum kilitlenmesi nedeniyle adım 1'de iptal edildiğinde işlem tekrar edilmesi için temel DataStoreWrapper geçer, böylece oturum kilitlenmesi nedeniyle işlem tekrar edilir.

Ayrıca, özel bir hata mesajı tüketiciye geri gönderilir, böylece oyuncu veri sistemi, oturum kilitleme durumunda müşteriye alternatif bir hata rapor edebilir.

Uyarılar

Oturum kilitleme rejimi, kilidini daima bir anahtar üzerinde serbest bırakan bir sunucuya güvenir ve bunu yaptıktan sonra daima kilidini serbest bırakır.Bu, asla son yazımın bir parçası olarak anahtarın kilidini açmak için bir talimatla olmalıdır PlayerRemoving veya BindToClose() .

Ancak kilidin bazı durumlarda başarısız olması mümkündür. Örneğin:

  • Sunucu çöktü veya DataStoreService anahtıya erişmeye çalışan tüm anahtarişlevsiz kaldı.
  • Mantıkta veya benzer bir hatadan dolayı, anahtarın kilidini açma talimatı verilmedi.

Bir anahtarın kilidini korumak için, hafızada yüklendiği süre boyunca düzenli olarak erişmelisiniz.Bu, normalde arka plandaki çoğu oyuncu veri sisteminde çalışan otomatik kaydetme döngüsünün bir parçası olarak yapılır, ancak bu sistem ayrıca manuel olarak yapmanız gerekirse bir refreshLockAsync yöntemi de açar.

Kilitin süresi dolduysa ve kilit güncellenmediyse, herhangi bir sunucu kilidi devralabilir.Farklı bir sunucu kilidi alırsa, mevcut sunucunun anahtarı okumak veya yazmaya çalışması başarısız olur, yeni bir kilit oluşturmazsa.

Geliştirici Ürün işlemesi

Tek boyutlu:

Arka Plan

ProcessReceipt geri arama, bir satın alma işleminin ne zaman sona ereceğini belirleme kritik görevi yerine getirir.ProcessReceipt çok spesifik senaryolarda çağrılır.Garantilerinin seti için bakın MarketplaceService.ProcessReceipt .

Bir satın alma işleminin tanımı deneyimler arasında değişebilir olsa da, aşağıdaki kriterleri kullanıyoruz

  1. Satın alma daha önce ele alınmadı.

  2. Satın alma, mevcut oturumda yansıtılır.

  3. Satın alma bir DataStore 'ye kaydedildi.

    Her satın alma, hatta tek seferlik tüketimler bile, DataStore içinde yansıtılmalıdır, böylece kullanıcıların satın alma geçmişi oturum verileriyle birlikte dahil edilir.

Bunun için, PurchaseGranted döndürmeden önce aşağıdaki işlemlerin yapılması gerekir:

  1. PurchaseId henüz ele alınmış olarak kaydedilmediğini doğrula.
  2. Oyuncunun hafızadaki oyuncu verilerinde satın alma ödülü verin.
  3. Oyuncunun hafızadaki oyuncu verilerinde ele alınan PurchaseId kaydedin.
  4. Oyuncunun hafızadaki oyuncu verilerini DataStore 'ye yazın.

Oturum kilitleme, önümüzdeki senaryolarla ilgilenmenize gerek kalmadığından bu akışı basitleştirir:

  • Mevcut sunucudaki bellekteki oyuncu verilerinin zaman aşımı olması muhtemel, böylece DataStore tarihini doğrulamadan önce en son değeri almanız gerekiyor PurchaseId geçmiş
  • Aynı satın alma işleminin başka bir sunucuda yürütülmesi için geri çağrısı, her ikisinin de okumasını ve yazmasını gerektiren PurchaseId geçmişi ve güncellenmiş oyuncu verilerini satın alma koşullarını önlemek için atomik olarak kaydetmesi gereken satın alma verilerini kaydetmesi gerekir

Oturum kilitleme, oyuncunun DataStore 'sine yazma denemesinin başarılı olması durumunda, bu sunucuda yüklenen ve kaydedilen veriler arasında oyuncunun DataStore 'sine başka sunucu tarafından başarıyla okunmadığını veya yazılmadığını garanti eder.Kısacası, bu sunucudaki bellekteki oyuncu verileri mevcut en güncel sürümdür.Bazı uyarılar var, ancak bu davranışı etkilemiyorlar.

Yaklaşım

ReceiptProcessor çerçevesindeki yorumlar yaklaşımı açıklar:

  1. oyuncuverilerinin şu anda bu sunucuda yüklendiğini ve herhangi bir hata olmadan yüklendiğini doğrula.

    Bu sistem oturum kilitleme kullandığından, bu kontrol ayrıca hafızadaki verilerin en yeni sürüm olduğunu da doğrular.

    Oyuncunun verileri henüz yüklenmediyse (bir oyuncu bir oyuna katıldığında bekleniyor), oyuncunun verilerinin yüklenmesini bekleyin.Sistem ayrıca verileri yüklenmeden önce oyuncunun oyundan ayrılmasını dinler, çünkü süresiz olarak verilmez ve oyuncu tekrar bu sunucuda bu satın alma işlemi için bu geri çağrı yapılırsa engellenirse.

  2. PurchaseId zaten oyuncu verilerinde işlenmiş olarak kaydedilmediğini doğrula.

    Oturum kilitlemesi nedeniyle, sistemin hafızada bulundurduğu PurchaseIds düzenek en yeni sürümdür.Eğer PurchaseId işlenmiş olarak kaydedilir ve DataStore 'a yüklendi veya kaydedildi bir değerde yansıtılırsa, PurchaseGranted geri dönün.Süreç olarak kaydedilirse, ancak içinde yansıtılmazsa , DataStore geri dönün NotProcessedYet .

  3. Oyuncu Verilerini bu sunucuda yerel olarak güncelle, satın almayı "ödüllendir".

    ReceiptProcessor genel bir geri arama yaklaşımı alır ve her birine farklı bir geri arama atar DeveloperProductId .

  4. Oyuncu verilerini bu sunucuda yerel olarak güncelle, böylece PurchaseId saklanabilir.

  5. Hafızadaki verileri kaydetmek için bir istek gönderin DataStore , istek başarılıysa PurchaseGranted geri döndürün. Aksi takdirde, NotProcessedYet geri dönün.

    Bu kaydetme isteği başarılı olmazsa, oyuncunun hafızadaki oturum verilerini kaydetmek için daha sonraki bir istek başarılı olabilir.Bir sonraki ProcessReceipt çağrı sırasında, adım 2 bu durumu ele alır ve PurchaseGranted döndürür.

Oyuncu verileri

Tek boyutlu nesneler: PlayerData.Server , PlayerData.Client

Arka Plan

Oyun kodu için bir arayüz sağlayan modüller, oyuncu oturum verilerini eşzamanlı olarak okumak ve yazmak için Roblox deneyimlerinde yaygındır.Bu bölüm PlayerData.Server ve PlayerData.Client kapsar.

Yaklaşım

PlayerData.Server ve PlayerData.Client takip edilenele alın:

  1. oyuncuverilerini hafızaya yükleme, yüklenemediği durumlarda işlemleri içerecek şekilde
  2. Sunucu kodunun oyuncu verilerini sorgulayıp değiştirmesi için bir arayüz sağlama
  3. oyuncuverilerindeki değişiklikleri istemciye yansıtarak istemci kodunun erişebilmesi
  4. Müşteriye yükleme ve/veya kaydetme hatalarını yeniden yansıtarak hata diyalogları gösterebilmesi için
  5. Oyuncunun verilerini düzenli olarak kaydetmek, oyuncu ayrıldığında ve sunucu kapandığında

Oyuncu verilerini yükle

An process diagram illustrating the loading system
  1. SessionLockedDataStoreWrapper veri mağazabir getAsync istek gönderir.

    Bu istek başarısız olursa, varsayılan veriler kullanılır ve profil daha sonra veri depolarına yazılmayacağından "hatalı" olarak işaretlenir.

    Alternatif bir seçenek, oyuncuyu kovmaktır, ancak oyuncunun varsayılan verilerle oynamasına ve olanlarla ilgili mesajları temizlemesine izin veriyoruz, deneyimden kaldırmak yerine.

  2. Yüklenen veriler ve hata durumu içeren bir başlangıç ​​yükü PlayerDataClient gönderilir (varsa).

  3. Oyuncu için waitForDataLoadAsync kullanarak elde edilen herhangi bir işlem yeniden başlatılır.

Sunucu koduna bir arayüz sağla

  • PlayerDataServer aynı çevrede çalışan herhangi bir sunucu kodu tarafından gerekli olabilen ve erişilebilen bir tek oyuncudur.
  • Oyuncu verileri bir anahtar ve değer sözlüğüne düzenlenir.Bu değerleri setValue, getValue, updateValue ve removeValue yöntemlerini kullanarak sunucuda manipüle edebilirsiniz.Bu yöntemlerin tümü, teslim olmadan senkronik olarak çalışır.
  • hasLoaded ve waitForDataLoadAsync yöntemleri, verilerin siz erişmeden önce yüklendiğinden emin olmak için mevcuttur.Diğer sistemler başlamadan önce bir yükleme ekranı sırasında bunu bir kez yapmanızı öneririz, böylece veri üzerindeki her etkileşimden önce yük hatalarını kontrol etmek zorunda kalmazsınız.
  • Bir hasErrored yöntemi, oyuncunun başlangıç yüklemesi başarısız olduğunda sorabilir ve böylece varsayılan verileri kullanmasına neden olur.Oyuncunun herhangi bir satın alma işlemi yapmasına izin vermeden önce bu yöntemi kontrol edin, çünkü satın alımlar başarılı bir yükleme olmadan veriye kaydedilemez.
  • Bir playerDataUpdated sinyali, oyuncunun verileri değiştiğinde her zaman player, key ve value ateş eder.Bireysel sistemler buna abone olabilir.

Müşteriye değişiklikleri yeniden yansıt

  • Oyuncu verilerindeki herhangi bir değişiklik PlayerDataServer 'a yansıtılır, bu anahtar setValueAsPrivate kullanılarak özel olarak işaretlenmediği sürece PlayerDataClient 'a
    • setValueAsPrivate , istemciye gönderilmemesi gereken anahtarları belirtmek için kullanılır
  • PlayerDataClient bir anahtarın değerini almak için bir yöntem içerir (alın) ve güncellendiğinde ateşleyen bir sinyal (güncellendi).Bir hasLoaded yöntemi ve bir loaded sinyali de dahil edildi, böylece müşteri, sistemlerini başlatmadan önce verilerin yüklenmesini ve yeniden yayınlanmasını bekleyebilir
  • PlayerDataClient aynı çevrede çalışan herhangi bir müşteri kodu tarafından gerekli olabilen ve erişilebilen bir tek oyuncudur

Müşteriye hataları yeniden yansıt

  • Oyuncu verilerini kaydedip yükleme sırasında karşılaşılan hata durumları PlayerDataClient 'ye yeniden yansıtılır.
  • Bu bilgiye getLoadError ve getSaveError yöntemleri ile birlikte, loaded ve saved sinyalleri ile erişin.
  • İki tür hata vardır: DataStoreError (DataStoreService isteği başarısız oldu) ve SessionLocked (bakınız Oturum Kilitleme ).
  • Bu olayları kullanarak müşteri satın alma istemlerini devre dışı bırakın ve uyarı diyaloglarını uygulayın. Bu resim bir örnek diyalog gösterir:
A screenshot of an example warning that could be shown when player data fails to load

Oyuncu verilerini kaydet

A process diagram illustrating the saving system
  1. Oyuncu oyundan ayrıldığında, sistem aşağıdaki adımları izler:

    1. Oyuncunun verilerini veri mağazayazmak güvenli olup olmadığını kontrol edin.Tehlikeli olabilecek senaryolar, oyuncunun verilerinin yüklenememesi veya hala yükleniyor olmasını içerir.
    2. SessionLockedDataStoreWrapper aracılığıyla bir istek göndererek mevcut bellekteki veri değerini veri deposuna yazın ve oturum kilidini bir kez tamamlandıktan sonra kaldırın.
    3. oyuncuverilerini (ve metadat ve hata durumları gibi diğer değişkenler) sunucu belleğinden temizler.
  2. Düzenli bir döngüde, sunucu her oyuncunun verilerini veri depolarına yazar (kaydetmek güvenliyse).Bu hoşgeldin redundansı, bir sunucu kazası durumunda kaybı azaltır ve ayrıca oturum kilidini korumak için gereklidir.

  3. Sunucuyu kapatma isteği alındığında, aşağıdaki şey bir BindToClose geri arama sırasında gerçekleşir:

    1. Bir istek, her oyuncunun verilerini sunucuda saklamak için yapılır, bir oyuncu sunucudan ayrıldığında normalde geçen süreçten sonra.Bu istekler paralel olarak yapılır, çünkü BindToClose geri çağrılarının bitmesi için sadece 30 saniye vardır.
    2. Kaydetmeleri hızlandırmak için, her anahtarın sırasındaki tüm diğer istekler temizlenir (bakın Yeniden denemeler ).
    3. Geri çağrı, tüm istekler tamamlanana kadar geri dönmez.