তথ্য সংরক্ষণ

শুরু করার আগে

Realtime Database ব্যবহার করার আগে, আপনাকে যা করতে হবে তা হলো:

  • আপনার ইউনিটি প্রজেক্টটি রেজিস্টার করুন এবং ফায়ারবেস ব্যবহারের জন্য কনফিগার করুন।

    • আপনার ইউনিটি প্রজেক্টে যদি আগে থেকেই ফায়ারবেস ব্যবহার করা হয়, তাহলে এটি ফায়ারবেসের জন্য ইতোমধ্যেই নিবন্ধিত এবং কনফিগার করা আছে।

    • আপনার যদি কোনো ইউনিটি প্রজেক্ট না থাকে, তাহলে আপনি একটি স্যাম্পল অ্যাপ ডাউনলোড করতে পারেন।

  • আপনার ইউনিটি প্রজেক্টে Firebase Unity এসডিকে (বিশেষত, FirebaseDatabase.unitypackage ) যোগ করুন।

মনে রাখবেন যে, আপনার ইউনিটি প্রজেক্টে ফায়ারবেস যোগ করার জন্য Firebase কনসোল এবং আপনার খোলা ইউনিটি প্রজেক্ট উভয় স্থানেই কাজ করতে হয় (উদাহরণস্বরূপ, আপনাকে কনসোল থেকে ফায়ারবেস কনফিগারেশন ফাইল ডাউনলোড করতে হবে, তারপর সেগুলোকে আপনার ইউনিটি প্রজেক্টে স্থানান্তর করতে হবে)।

ডেটা সংরক্ষণ করা হচ্ছে

Firebase Realtime Database ডেটা লেখার পাঁচটি পদ্ধতি রয়েছে:

পদ্ধতি সাধারণ ব্যবহার
SetValueAsync() একটি নির্দিষ্ট পাথে, যেমন users/<user-id>/<username> , ডেটা লিখুন বা প্রতিস্থাপন করুন।
SetRawJsonValueAsync() সরাসরি Json দিয়ে ডেটা লিখুন বা প্রতিস্থাপন করুন, যেমন users/<user-id>/<username>
Push() ডেটার তালিকায় যোগ করুন। প্রতিবার আপনি Push() কল করলে, Firebase একটি অনন্য কী (key) তৈরি করে যা একটি অনন্য শনাক্তকারী (unique identifier) ​​হিসেবেও ব্যবহার করা যেতে পারে, যেমন user-scores/<user-id>/<unique-score-id>
UpdateChildrenAsync() সমস্ত ডেটা প্রতিস্থাপন না করে একটি নির্দিষ্ট পাথের কিছু কী আপডেট করুন।
RunTransaction() একই সাথে একাধিক আপডেটের কারণে ক্ষতিগ্রস্ত হতে পারে এমন জটিল ডেটা আপডেট করুন।

একটি ডেটাবেস রেফারেন্স পান

ডেটাবেসে ডেটা লিখতে হলে, আপনার DatabaseReference এর একটি ইনস্ট্যান্স প্রয়োজন।

using Firebase;
using Firebase.Database;

public class MyScript: MonoBehaviour {
  void Start() {
    // Get the root reference location of the database.
    DatabaseReference reference = FirebaseDatabase.DefaultInstance.RootReference;
  }
}

একটি রেফারেন্সে ডেটা লিখুন, আপডেট করুন বা মুছুন।

মৌলিক লেখার ক্রিয়াকলাপ

সাধারণ রাইট অপারেশনের জন্য, আপনি SetValueAsync() ব্যবহার করে একটি নির্দিষ্ট রেফারেন্সে ডেটা সেভ করতে পারেন, যা ওই পাথে থাকা যেকোনো বিদ্যমান ডেটাকে প্রতিস্থাপন করে। উপলব্ধ JSON টাইপগুলোর সাথে সঙ্গতিপূর্ণ টাইপগুলো পাস করার জন্য আপনি এই মেথডটি নিম্নরূপে ব্যবহার করতে পারেন:

  • string
  • long
  • double
  • bool
  • Dictionary<string, Object>
  • List<Object>

আপনি যদি একটি টাইপড C# অবজেক্ট ব্যবহার করেন, তাহলে আপনি বিল্ট-ইন JsonUtility.ToJson() ব্যবহার করে অবজেক্টটিকে র Json-এ রূপান্তর করতে পারেন এবং SetRawJsonValueAsync() কল করতে পারেন। উদাহরণস্বরূপ, আপনার একটি User ক্লাস থাকতে পারে যা দেখতে নিম্নরূপ:

public class User {
    public string username;
    public string email;

    public User() {
    }

    public User(string username, string email) {
        this.username = username;
        this.email = email;
    }
}

আপনি SetRawJsonValueAsync() ব্যবহার করে নিম্নলিখিত উপায়ে একজন ব্যবহারকারী যোগ করতে পারেন:

private void writeNewUser(string userId, string name, string email) {
    User user = new User(name, email);
    string json = JsonUtility.ToJson(user);

    mDatabaseRef.Child("users").Child(userId).SetRawJsonValueAsync(json);
}

এইভাবে SetValueAsync() বা SetRawJsonValueAsync() ব্যবহার করলে নির্দিষ্ট স্থানের ডেটা ওভাররাইট হয়ে যায়, যার মধ্যে যেকোনো চাইল্ড নোডও অন্তর্ভুক্ত থাকে। তবে, আপনি পুরো অবজেক্টটি পুনরায় না লিখেও একটি চাইল্ডকে আপডেট করতে পারেন। আপনি যদি ব্যবহারকারীদের তাদের প্রোফাইল আপডেট করার অনুমতি দিতে চান, তাহলে আপনি নিম্নলিখিতভাবে ইউজারনেম আপডেট করতে পারেন:

mDatabaseRef.Child("users").Child(userId).Child("username").SetValueAsync(name);

ডেটার তালিকায় যুক্ত করুন

মাল্টিইউজার অ্যাপ্লিকেশনে কোনো লিস্টে ডেটা যুক্ত করতে Push() মেথডটি ব্যবহার করুন। নির্দিষ্ট Firebase রেফারেন্সে যখনই কোনো নতুন চাইল্ড যুক্ত করা হয়, Push() মেথডটি একটি ইউনিক কী তৈরি করে। লিস্টের প্রতিটি নতুন এলিমেন্টের জন্য এই স্বয়ংক্রিয়ভাবে তৈরি হওয়া কীগুলো ব্যবহার করে, একাধিক ক্লায়েন্ট রাইট কনফ্লিক্ট ছাড়াই একই সময়ে একই লোকেশনে চাইল্ড যুক্ত করতে পারে। Push() দ্বারা তৈরি ইউনিক কী-টি একটি টাইমস্ট্যাম্পের উপর ভিত্তি করে তৈরি হয়, ফলে লিস্টের আইটেমগুলো স্বয়ংক্রিয়ভাবে কালানুক্রমিকভাবে সাজানো থাকে।

Push() মেথড দ্বারা ফেরত আসা নতুন ডেটার রেফারেন্স ব্যবহার করে আপনি চাইল্ডের স্বয়ংক্রিয়ভাবে তৈরি হওয়া কী-এর মান পেতে পারেন অথবা চাইল্ডের জন্য ডেটা সেট করতে পারেন। Push() রেফারেন্সের উপর Key কল করলে স্বয়ংক্রিয়ভাবে তৈরি হওয়া কী-এর মান ফেরত আসে।

নির্দিষ্ট ক্ষেত্রগুলি আপডেট করুন

কোনো নোডের অন্যান্য চাইল্ড নোড ওভাররাইট না করে একই সাথে নির্দিষ্ট চাইল্ড নোডগুলোতে লেখার জন্য, UpdateChildrenAsync() মেথডটি ব্যবহার করুন।

UpdateChildrenAsync() কল করার সময়, আপনি কী-এর জন্য একটি পাথ নির্দিষ্ট করে নিম্ন-স্তরের চাইল্ড ভ্যালুগুলো আপডেট করতে পারেন। যদি আরও ভালোভাবে স্কেল করার জন্য ডেটা একাধিক স্থানে সংরক্ষিত থাকে, তাহলে আপনি ডেটা ফ্যান-আউট ব্যবহার করে সেই ডেটার সমস্ত ইনস্ট্যান্স আপডেট করতে পারেন। উদাহরণস্বরূপ, একটি গেমে এইরকম একটি LeaderboardEntry ক্লাস থাকতে পারে:

public class LeaderboardEntry {
    public string uid;
    public int score = 0;

    public LeaderboardEntry() {
    }

    public LeaderboardEntry(string uid, int score) {
        this.uid = uid;
        this.score = score;
    }

    public Dictionary<string, Object> ToDictionary() {
        Dictionary<string, Object> result = new Dictionary<string, Object>();
        result["uid"] = uid;
        result["score"] = score;

        return result;
    }
}

একটি লিডারবোর্ড এন্ট্রি তৈরি করতে এবং একই সাথে সেটিকে সাম্প্রতিক স্কোর ফিড ও ব্যবহারকারীর নিজের স্কোর তালিকা দিয়ে আপডেট করতে, গেমটি এই ধরনের কোড ব্যবহার করে:

private void WriteNewScore(string userId, int score) {
    // Create new entry at /user-scores/$userid/$scoreid and at
    // /leaderboard/$scoreid simultaneously
    string key = mDatabase.Child("scores").Push().Key;
    LeaderBoardEntry entry = new LeaderBoardEntry(userId, score);
    Dictionary<string, Object> entryValues = entry.ToDictionary();

    Dictionary<string, Object> childUpdates = new Dictionary<string, Object>();
    childUpdates["/scores/" + key] = entryValues;
    childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

    mDatabase.UpdateChildrenAsync(childUpdates);
}

এই উদাহরণে Push() ব্যবহার করে /scores/$key তে সমস্ত ব্যবহারকারীর এন্ট্রি ধারণকারী নোডে একটি এন্ট্রি তৈরি করা হয় এবং একই সাথে Key দিয়ে কী-টি পুনরুদ্ধার করা হয়। এরপর সেই কী-টি ব্যবহার করে /user-scores/$userid/$key তে ব্যবহারকারীর স্কোরে একটি দ্বিতীয় এন্ট্রি তৈরি করা যেতে পারে।

এই পাথগুলো ব্যবহার করে, আপনি UpdateChildrenAsync() -এর একটিমাত্র কলের মাধ্যমে JSON ট্রি-এর একাধিক স্থানে একযোগে আপডেট করতে পারেন, যেমনটি এই উদাহরণে উভয় স্থানেই নতুন এন্ট্রি তৈরি করা হয়েছে। এইভাবে করা একযোগে আপডেটগুলো অ্যাটমিক হয়: হয় সব আপডেট সফল হয় অথবা সব আপডেট ব্যর্থ হয়।

ডেটা মুছে ফেলুন

ডেটা মুছে ফেলার সবচেয়ে সহজ উপায় হলো সেই ডেটার অবস্থানের রেফারেন্সের উপর RemoveValue() কল করা।

আপনি SetValueAsync() বা UpdateChildrenAsync() এর মতো অন্য কোনো রাইট অপারেশনের ভ্যালু হিসেবে null উল্লেখ করেও ডিলিট করতে পারেন। UpdateChildrenAsync() এর সাথে এই কৌশলটি ব্যবহার করে আপনি একটিমাত্র API কলে একাধিক চাইল্ড ডিলিট করতে পারেন।

আপনার ডেটা কখন সংরক্ষিত হচ্ছে তা জানুন।

আপনার ডেটা কখন Firebase Realtime Database সার্ভারে কমিট করা হচ্ছে তা জানতে, আপনি একটি কন্টিনিউয়েশন যোগ করতে পারেন। SetValueAsync() এবং UpdateChildrenAsync() উভয়ই একটি Task রিটার্ন করে, যা আপনাকে অপারেশনটি কখন সম্পূর্ণ হয়েছে তা জানতে সাহায্য করে। যদি কোনো কারণে কলটি অসফল হয়, তাহলে Task-এর IsFaulted প্রপার্টির মান true হবে এবং এর Exception প্রপার্টিটি ব্যর্থতার কারণ নির্দেশ করবে।

লেনদেন হিসেবে ডেটা সংরক্ষণ করুন

যখন এমন ডেটা নিয়ে কাজ করা হয় যা যুগপৎ পরিবর্তনের ফলে নষ্ট হয়ে যেতে পারে, যেমন ইনক্রিমেন্টাল কাউন্টার, তখন আপনি একটি ট্রানজ্যাকশন অপারেশন ব্যবহার করতে পারেন। এই অপারেশনের জন্য আপনি একটি Func দেন। এই আপডেট Func আর্গুমেন্ট হিসেবে ডেটার বর্তমান অবস্থা গ্রহণ করে এবং আপনি যে নতুন কাঙ্ক্ষিত অবস্থাটি লিখতে চান, তা রিটার্ন করে। যদি আপনার নতুন মান সফলভাবে লেখার আগেই অন্য কোনো ক্লায়েন্ট সেই লোকেশনে লেখে, তাহলে আপনার আপডেট ফাংশনটি নতুন বর্তমান মান দিয়ে আবার কল করা হয় এবং লেখার চেষ্টাটি পুনরায় করা হয়।

উদাহরণস্বরূপ, একটি গেমে আপনি ব্যবহারকারীদের সর্বোচ্চ পাঁচটি স্কোর দিয়ে একটি লিডারবোর্ড আপডেট করার সুযোগ দিতে পারেন:

private void AddScoreToLeaders(string email, 
                               long score,
                               DatabaseReference leaderBoardRef) {

    leaderBoardRef.RunTransaction(mutableData => {
      List<object> leaders = mutableData.Value as List<object>

      if (leaders == null) {
        leaders = new List<object>();
      } else if (mutableData.ChildrenCount >= MaxScores) {
        long minScore = long.MaxValue;
        object minVal = null;
        foreach (var child in leaders) {
          if (!(child is Dictionary<string, object>)) continue;
          long childScore = (long)
                      ((Dictionary<string, object>)child)["score"];
          if (childScore < minScore) {
            minScore = childScore;
            minVal = child;
          }
        }
        if (minScore > score) {
          // The new score is lower than the existing 5 scores, abort.
          return TransactionResult.Abort();
        }

        // Remove the lowest score.
        leaders.Remove(minVal);
      }

      // Add the new high score.
      Dictionary<string, object> newScoreMap =
                       new Dictionary<string, object>();
      newScoreMap["score"] = score;
      newScoreMap["email"] = email;
      leaders.Add(newScoreMap);
      mutableData.Value = leaders;
      return TransactionResult.Success(mutableData);
    });
}

ট্রানজ্যাকশন ব্যবহার করলে লিডারবোর্ড ভুল হওয়া থেকে রক্ষা পায়, যদি একাধিক ব্যবহারকারী একই সময়ে স্কোর রেকর্ড করে অথবা ক্লায়েন্টের কাছে পুরোনো ডেটা থাকে। যদি ট্রানজ্যাকশনটি প্রত্যাখ্যাত হয়, সার্ভার বর্তমান মানটি ক্লায়েন্টকে ফেরত পাঠায়, যা আপডেট করা মান দিয়ে ট্রানজ্যাকশনটি আবার চালায়। এই প্রক্রিয়াটি ততক্ষণ চলতে থাকে যতক্ষণ না ট্রানজ্যাকশনটি গৃহীত হয় অথবা অনেক বেশিবার চেষ্টা করা হয়ে যায়।

অফলাইনে ডেটা লিখুন

যদি কোনো ক্লায়েন্ট তার নেটওয়ার্ক সংযোগ হারায়, আপনার অ্যাপটি সঠিকভাবে কাজ করতে থাকবে।

ফায়ারবেস ডেটাবেসের সাথে সংযুক্ত প্রতিটি ক্লায়েন্ট তার সক্রিয় ডেটার নিজস্ব একটি অভ্যন্তরীণ সংস্করণ বজায় রাখে। যখন ডেটা লেখা হয়, তখন তা প্রথমে এই স্থানীয় সংস্করণে লেখা হয়। এরপর ফায়ারবেস ক্লায়েন্ট সেই ডেটা দূরবর্তী ডেটাবেস সার্ভার এবং অন্যান্য ক্লায়েন্টদের সাথে সর্বাত্মক প্রচেষ্টার ভিত্তিতে সিঙ্ক্রোনাইজ করে।

এর ফলে, সার্ভারে কোনো ডেটা লেখার আগেই, ডেটাবেসে করা সমস্ত রাইট অপারেশন তাৎক্ষণিকভাবে লোকাল ইভেন্ট ট্রিগার করে। এর মানে হলো, নেটওয়ার্ক ল্যাটেন্সি বা কানেক্টিভিটি নির্বিশেষে আপনার অ্যাপটি রেসপন্সিভ থাকে।

সংযোগ পুনঃপ্রতিষ্ঠিত হলে, আপনার অ্যাপটি প্রয়োজনীয় ইভেন্টগুলো পেয়ে যায়, যার ফলে কোনো কাস্টম কোড না লিখেই ক্লায়েন্ট বর্তমান সার্ভার অবস্থার সাথে সিঙ্ক হয়ে যায়।

পরবর্তী পদক্ষেপ