به چالش کشیدن دوستان (Challenge)

دقت کنید که این مستند مخصوص رقابت آنلاین است و برای نیازمندی‌های چت هیچ کاربردی ندارد.

برای آنکه یک رقابت آنلاین را بین کاربران خود در بکتوری اجرا کنید، ابتدا باید کاربرانی که در این رقابت شرکت کرده‌اند را مشخص کنید. برای اینکه یا بایستی کاربران با درخواست Match-Making با دیگر کاربران آنلاین هم‌سطح با خود (به صورت تصادفی) بازی کنند، و یا اینکه کاربران خاص دیگری را به چالش بکشند.

در این مستند، ما قصد داریم نحوه به چالش کشیدن کاربران دیگر را شرح دهیم. در ادامه ارسال درخواست چالش به دوستان، رد یا پذیرش چالش درخواستی و شروع یک رقابت آنلاین (Match) با این کاربران را خواهیم دید.

پیش‌نیاز

  1. در صورتی که با سرویس بلادرنگ آشنایی ندارید، به معرفی سرویس بلادرنگ مراجعه کنید.
  2. در صورتی که هنوز با تنظیمات پنل سرویس بلادرنگ آشنا نشده اید، به تنظیمات پنل سرویس بلادرنگ مراجعه کنید.
  3. اگر هنوز SDK یونیتی را راه‌اندازی نکرده‌اید، به راه‌اندازی SDK یونیتی مراجعه کنید.
  4. در صورتی که با نحوه‌ی اتصال به سرویس بلادرنگ و ایجاد یک چت ساده به کمک آن آشنایی ندارید، مستند اتصال ساده و ارسال پیام را حتما مطالعه کنید.

تعریف چالش

در سرویس بلادرنگ، چالش (Challenge) به درخواستی گفته می‌شود که یک کاربر موبایل می‌تواند به تعدادی کاربر دیگر با داشتن UserId آنها بفرستد، در این حالت کاربران دیگر می‌توانند چالش را بپذیرند یا رد کنند. در صورتی که تعداد کسانی که چالش را پذیرفته‌اند به حد نصاب برسد، چالش موفق بوده و کاربران می‌توانند رقابت آنلاین خود را آغاز کنند. در ادامه نحوه انجام تمامی این کارها را خواهیم دید.

ارسال چالش به دیگران

برای ارسال یک چالش به چند کاربر دیگر کافیست بعد از آنکه اتصال به سرویس بلادرنگ برقرار گردید، کدی مانند زیر اجرا کنید:

List<string> challengedUsers = new List<string> {
        "<USER-ID-1>", "<USER-ID-2>", "<USER-ID-3>" };

// Create an instance of BacktoryChallenge for later uses
BacktoryChallenge requestedChallenge;

// Send challenge to users
BacktoryChallenge.CreateNew(challengedUsers, 2, 25, (response) => {
    // Check if sending challenge to backtory was successful or not
    if (response.Successful) {
        Debug.Log("Your challenge is received by backtory.");
        requestedChallenge = response.Body;
    } else {
        Debug.Log("Operation failed with code: "
            + response.Code
            + " and message: "
            + response.Message);
    }
}, "challengeName", "metaData");

همانطور که می‌بینید تابع CreateNew یک درخواست چالش را ارسال می‌کند. این تابع شش پارامتر دارد که به ترتیب عبارتند از:

  1. usersToChallenge: لیست شناسه کاربران به چالش کشیده شده.
  2. minPlayersToStart: حداقل تعداد کاربران برای آنکه چالش پذیرفته شود. در صورتی که این فیلد را null دهید، همه کاربران باید درخواست را بپذیرند.
  3. expirationTime: مدت زمانی (به ثانیه) که کاربران دیگر وقت دارند به چالش پاسخ دهند. دقت کنید که این زمان می‌تواند حداکثر ۱۵ دقیقه باشد و در صورتی که عدد بیشتری به تابع پاس داده شود، سرور همان ۱۵ دقیقه را در نظر خواهد گرفت.
  4. challengeName: نامی که برای چالش انتخاب می‌کنید.
  5. metaData: رشته‌ای شامل هر اطلاعات اضافی‌ای که می‌خواهید همراه با چالش ارسال کنید.
  6. callback: تابعی که با دریافت پاسخ از سمت سرور بکتوری صدا زده می‌شود.

مهم: به طور پیشفرض فردی که چالش را مطرح می‌کند، آن را پذیرفته است. یعنی اگر مقدار minPlayersToStart برابر ۲ باشد، تنها کافیست یک نفر دیگر چالش را بپذیرد.

لغو کردن درخواست چالش

ممکن است بعد از آن که برای یک کاربر درخواست چالش ارسال کردید، بخواهید درخواست خود را پس گرفته و کنسل کنید. برای همین هدف، در تکه کد سابق، شیء BacktoryChallengeای را که ایجاد کرده بودیم، نگه داشتیم. حال برای لغو درخواست می‌توانید مطابق با کد زیر عمل کنید:

// We have ``requestedChallenge'' object from previous part
requestedChallenge.Cancel((response) =>
{
    if (response.Successful) {
        Debug.Log("Challenge request canceled successfully.");
    } else {
        Debug.Log("Operation failed with code: "
            + response.Code
            + " and message: "
            + response.Message);
    }
});

آگاهی از وضعیت چالش (Challenge-Listener)

جهت پذیرش چالش‌های دیگران، باید Listenerهایی که در زیر می‌آید، را با توابع خودتان مقداردهی کنید. این Listenerها زمانی که چالشی از طرف کاربران دیگر برای شما ارسال شود، آن را دریافت خواهند کرد و به شما اجازه می‌دهند که در مورد آن تصمیم‌گیری کنید.

برای اضافه کردن این Listenerها کدی مانند زیر لازم است:

BacktoryChallenge.SetOnChallengeInvitationListener((challenge) =>
{
    // 1. TODO: If you are challenged, handle it here
});
BacktoryChallenge.SetOnChallengeFailedListener((message) =>
{
    // 2. TODO: If your challenge failed, handle it here
});
BacktoryChallenge.SetOnChallengeAcceptedListener((message) =>
{
    // 3. TODO: If your friends accepted your challenge, handle it here
});
BacktoryChallenge.SetOnChallengeRejectedListener((message) =>
{
    // 4. TODO: If your friends declined your challenge, handle it here
});
BacktoryChallenge.SetOnChallengeReadyListener((match) =>
{
    // 5. TODO: If challenge is ready, handle it here
});

شما باید هر یک از توابعی که مورد نیازتان است را پیاده‌سازی کنید. توابعی که امکان پیاده‌سازی آنها هست عبارتند از:

دریافت چالش از دیگران

در صورتی که یک چالش از کاربر دیگری برای شما ارسال شود، به کمک تابع OnChallengeInvitationListener به شما خبر داده می‌شود. در واقع، این تابع شیء BacktoryChallengeای که توسط چالش‌کننده ایجاد شده است، را به شما می‌دهد. کد زیر دریافت چالش و نمایش اطلاعات آن را انجام می‌دهد:

BacktoryChallenge invitedChallenge;

BacktoryChallenge.SetOnChallengeInvitationListener((BacktoryChallenge challenge) =>
{
    Debug.Log("Invited to the challenge with id: " + challenge.ChallengeId);
    Debug.Log("You are challenged by: " + challenge.ChallengerUserId);

    Debug.Log("List of challenged users are: ");
    for (int i = 0; i < challenge.ChallengedUsers.Count; i++) {
        Debug.Log(i + ": " + challenge.ChallengedUsers[i]);
    }

    invitedChallenge = challenge;
});

همانطور که می‌بینید، شما به شناسه‌ی کسی که چالش را آغاز کرده و لیست شناسه‌های تمام کسانی که چالش شده‌اند، دسترسی دارید. دقت کنید که در کد بالا یک ارجاع به شیء BacktoryChallenge دریافت شده، در بیرون از تابع نگه داشته شده است. این کار به خاطر آن است که برای پذیرفتن یا رد چالش، به شیء دریافت شده احتیاج داریم. در ادامه نحوه‌ی کار با این شیء BacktoryChallenge می‌آید.

لیست چالش‌های فعال

در صورتی که کاربر فعلی شما آفلاین بوده باشد، و در بازه آفلاین بودن، چالشی برای او ارسال شده باشد. به محض آنلاین شدن با کدی مانند زیر می‌تواند لیست چالش‌های فعال خود را دریافت کند:

BacktoryChallenge.ActiveChallenges ((response) =>
{
    if (response.Successful) {
        IList<BacktoryChallenge> challengeList = response.Body;
        Debug.Log("Your have " + challengeList.Count + " active challenges");
        for (int i = 0; i < challengeList.Count; i++) {
            Debug.Log("Challenged by: " + challengeList[i].ChallengerUserId);
            Debug.Log("Number of challenged users: " + challengeList[i].ChallengedUsers.Count);
        }
    } else {
        Debug.Log("Operation failed with code: "
            + response.Code
            + " and message: "
            + response.Message);
    }
});

همانطور که در کد بالا می‌بینید، به ازای هر چالش فعال، شناسه کاربری که چالش را ایجاد کرده (ChallengerUserId) و تعداد بازیکنانی که در این چالش شرکت کرده‌اند، چاپ شده‌اند.

پذیرش یک چالش

کاربر شما یا از طریق دریافت چالش‌های Offline و یا دریافت یک چالش آنلاین (یعنی دریافت یک چالش، لحظه‌ای که کاربر به چالش کشیده شده آنلاین است) به یک چالش دعوت می‌شود. در جواب کاربر موبایل باید چالش را بپذیرد و یا رد کند. پذیرش یک چالش ساده است و کافیست روی شیء BacktoryChallengeی که دریافت کرده‌اید، تابع Accept() را صدا بزنید. به عنوان یک مثال ساده، می‌خواهیم هر وقت یک چالش به صورت آنلاین برای کاربر ارسال شد، درجا بپذیرد. کافیست در Listener دریافت چالش کدی مانند زیر را بنویسیم:

BacktoryChallenge.SetOnChallengeInvitationListener((BacktoryChallenge invitedChallenge) =>
{
    invitedChallenge.Accept ("metaData", (response) =>
    {
        if (response.Successful) {
            Debug.Log("Invited challenge is accepted successfully.");
        } else {
            Debug.Log("Operation failed with code: "
                + response.Code
                + " and message: "
                + response.Message);
        }
    }
});

دریافت پذیرش از دیگران

در دریافت یک چالش آنلاین دیدید که چگونه می‌تواند از دریافت یک چالش آنلاین مطلع شد و در پذیرش یک چالش نیز دیدیم که چگونه چالش دریافتی را بپذیریم. قدم بعدی این است که، کاربری که چالش را مطرح کرده است، از اینکه چالشش پذیرفته شده است، اطلاع یابد. برای این کار کافیست تابع OnChallengeAcceptedListener را مانند کد زیر پیاده‌سازی کنید:

BacktoryChallenge.SetOnChallengeAcceptedListener((BacktoryChallengeAcceptedMessage message) =>
{
    Debug.Log("Accepted challenge id: " + message.ChallengeId);
    Debug.Log("Challenger user id: " + message.ChallengerId);
    Debug.Log("Challenge is accepted by: " + message.AcceptingUserId);
});

به همین سادگی می‌توان فهمید که چه کسی چه چالشی را پذیرفته است.

رد یک چالش

کاربر شما یا از طریق دریافت چالش‌های Offline و یا دریافت یک چالش آنلاین (یعنی دریافت یک چالش، لحظه‌ای که کاربر به چالش کشیده شده آنلاین است) به یک چالش دعوت می‌شود. در صورتی که کاربر شما تمایل به شرکت در چالش نداشته باشد، می‌تواند در جواب چالش دریافتی، آن را رد کند. برای مثال کد زیر به محض دریافت یک چالش آنلاین از طریق Listener دریافت چالش آن را رد می‌کند:

BacktoryChallenge.SetOnChallengeInvitationListener(
    (BacktoryChallenge invitedChallenge) =>
{
    invitedChallenge.Cancel((response) =>
    {
        // Check if your decline is sent to challenger user successfully or not
        if (response.Successful) {
            Debug.Log("Challenge Declined!!!");
        } else {
            Debug.Log("Operation failed, code: "
                + response.Code
                + " and message: "
                + response.Message);
        }
    });
});

دریافت رد از دیگران

در دریافت یک چالش آنلاین دیدید که چگونه می‌تواند از دریافت یک چالش آنلاین مطلع شد و در رد یک چالش نیز دیدیم که چگونه چالش دریافتی را رد کنیم. قدم بعدی این است که کاربری که چالش را مطرح کرده، از اینکه چالشش رد شده است، اطلاع یابد. برای این کار کافیست تابع OnChallengeRejectedListener را مانند کد زیر پیاده‌سازی کنید:

BacktoryChallenge.SetOnChallengeRejectedListener((BacktoryChallengeRejectedMessage message) =>
{
    Debug.Log("Rejected challenge id: " + message.ChallengeId);
    Debug.Log("Challenger user id: " + message.ChallengerId);
    Debug.Log("Challenge is rejected by: " + message.RejectingUserId);
});

به همین سادگی می‌توان فهمید که چه کسی چه چالشی را رد کرده است.

شکست خوردن چالش

یک چالش ممکن است به یکی از چهار دلیل زیر با شکست مواجه شود:

  1. در زمان تعیین شده برای چالش (expirationTime)، تعداد کافی از افراد چالش شده درخواست چالش را نپذیرند. در این حالت می‌گوییم آن چالش منقضی (Expired) شده است.
  2. چالشی که شما به آن دعوت شده بودید، موفق شود، ولی شما آن را رد کرده باشید. در این حالت می‌گوییم رخداد FormedWithoutYou اتفاق افتاده است.
  3. تعداد افرادی که چالش را رد کرده‌اند زیاد باشد، به طوری که حتی اگر همه‌ی افرادی که هنوز پاسخ به چالش نداده‌اند، آن را بپذیرند، باز هم حد نصاب آن‌ها به تعداد minPlayersToStart نرسد. در این حالت می‌گوییم رخداد ChallengeImpossible رخداده است.
  4. فرد درخواست دهنده‌ی چالش، قبل از این‌که افراد چالش‌شده درخواستش را بپذیرند، چالش را رد کند. در این حالت می‌گوییم رخداد Canceled اتفاق افتاده است.

در هر یک از این شرایط، تابع OnChallengeFailedListener زیر صدا زده می‌شود:

BacktoryChallenge.SetOnChallengeFailedListener((message) =>
{
    Debug.Log("Challenge with id " + message.ChallengeId + " failed.");
    Debug.Log("Challenge cause: " +
            Enum.GetName(typeof(BacktoryChallengeFailureCause), message.Cause);
});

همان‌طور که می‌بینید، از طریق مشخصه‌ی ChallengeId می‌توان فهمید که کدام یک از چالش‌های ما با شکست مواجه شده و از طریق مشخصه‌ی Cause علت آن را جویا شد. Cause یک enum از جنس BacktoryChallengeFailureMessage است که مقادیر آن همان چهار مقدار توضیح داده شده در بالا هستند. دقت کنید که حالت FormedWithoutYou صرفا برای اطلاع شما ارسال می‌شود و شاید بر خلاف دو حالت دیگر چندان کاربردی نباشد.

موفقیت یک چالش پذیرفته شده

در دریافت یک چالش دیدیم که چگونه می‌توانید از چالشی که برای کاربر شما ارسال می‌شود، خبردار شوید. در پذیریش یک چالش و رد یک چالش نیز نحوه پذیرش و یا رد یک چالش را دیدید. سرانجام، اگر تعداد افرادی که چالش را پذیرفته‌اند به حد نصاب minPlayersToStart برسد، آن چالش پذیرفته شده است و در این حالت کاربرانی که چالش را پذیرفته‌اند، باید به مرحله بعد که انجام رقابت آنلاین است، بروند. اگر کاربر فعلی شما، یکی از کسانی باشد که چالش را پذیرفته است، از طریق تابع OnChallengeReadyListener به شکل زیر از موفقیت چالش خبردار خواهد شد:

BacktoryMatch foundMatch;

BacktoryChallenge.SetOnChallengeReadyListener((BacktoryMatch match) =>
{
    Debug.Log("Backtory match id for this challenge: " + match.MatchId);
    Debug.Log("Challenge ready webhook response: " + match.ExtraMessage);

    Debug.Log("List of participants in match:");
    for (int i = 0; i < match.MatchParticipants.Count; i++)
    {
        Debug.Log("Participant no. " + i + ": " + match.MatchParticipants[i]);
    }

    foundMatch = match;
});

دقت کنید که در کد بالا یک ارجاع به شیء BacktoryMatch دریافت شده، در بیرون از تابع نگه داشته شده است. این کار به خاطر آن است که برای آغاز رقابت آنلاین به شیء دریافت شده احتیاج داریم. در انجام رقابت آنلاین خواهید دید که چگونه از این شیء BacktoryMatch استفاده خواهیم کرد.

به این ترتیب، همه کارهایی که می‌توان برای به چالش کشیدن کاربران دیگر انجام داد را دیدید. در یک اپلیکیشن واقعی کافیست یک واسط گرافیکی مناسب برای وضعیت چالش بگذارید و هر گاه که کسی چالش را می‌پذیرد و یا رد می‌کند، آن را به صورت زنده به‌روزرسانی کنید.

در بخش بعدی وارد رقابت آنلاین خواهیم شد، و می‌بینیم که پس از آنکه یک چالش پذیرفته می‌شود، چگونه باید یک رقابت آنلاین را آغاز کرد.

گام بعدی