> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qwacks.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ما الذي يجمعه اختبار اللعب

> الفيديو ونوافذ الأداء والاستثناءات ونماذج الملاحظات من بناء اختبار لعب في Unity، واستدعاءات C# لكلٍّ منها.

لا يعمل أيٌّ من هذه إلا ما دام اختبار اللعب يشغّله في Protokite **وقد سمح به اللاعب**. ولا يحتاج أيٌّ منها
إلى شيفرة: استدعاءات C# في هذه الصفحة للعبة تريد قوائمها أو أزرارها الخاصة حولها، وهي آمنة في كل بناء — فحين
لا يكون اختبار لعب قائمًا تجيب بـfalse أو null أو `TurnedOff`. ولا يعمل دونه إلا ثلاثة: الفيديو الاختباري، وإجابة
اللاعب عن سؤال الموافقة، ومعرّف Steam الذي يُحفظ للجلسة القادمة. وإعداد البناء مشروح في صفحة
[حزمة Unity](/ar/protokite/unity-package).

<h2 id="what-the-player-allowed">
  ما الذي سمح به اللاعب
</h2>

يسأل البناء لاعبه عمّا يجوز لاختبار اللعب أن يجمعه قبل أن يجمع أي شيء، وإجابته تقرّر ما تفعله بقية هذه
الصفحة:

| إجابته | تسجيل الفيديو | التحليلات الثقيلة |
| - | - | - |
| Record my screen and collect play data | يعمل | يعمل |
| Record my screen only | يعمل | مطفأ |
| Collect play data only | مطفأ | يعمل |
| Collect nothing | مطفأ | مطفأ، ولا تبدأ أي جلسة أصلًا |

وتجيب `ProtokitePlaytest.IsFeatureEnabled` عن الأمرين معًا: الميزة التي يشغّلها اختبار اللعب ويستثنيها
اللاعب تُقرأ مطفأة في كل مكان. والتقاط الاستثناءات يتبع بيانات اللعب هناك، أما [الاستثناءات](#exceptions) نفسها
فمن شأن حزمة Flock، تبلّغ عنها بإعدادها الخاص مهما أجاب اللاعب.

```csharp theme={null}
if (ProtokitePlaytest.IsFeatureEnabled(ProtokitePlaytestFeatures.VideoRecording))
    ShowTheRecordingIndicator();
```

<Note>
  **نموذج الملاحظات لا تشمله تلك الإجابة.** فهو لا يُرسل إلا حين يملؤه اللاعب ويضغط Send، وذلك من فعله في
  كل حال — والنموذج الذي لم يستطع تشغيلٌ سابق إرساله يُرسل لاحقًا، مهما أجاب اللاعب بعد ذلك.
</Note>

<h2 id="video-recording">
  تسجيل الفيديو
</h2>

تُسجَّل شاشة اللعبة منذ لحظة تحميل اختبار اللعب وسماح اللاعب بذلك، قبل أن يسجّل أحد دخوله: تسجيل واحد لكل تشغيل، يُكتب أثناء
التسجيل في ملف WebM يشغّله المتصفّح دون تثبيت أي شيء. ويُستثنى الوقت الذي تقضيه اللعبة في الخلفية.

الإعدادات في **Protokite → Playtest → Settings**، تحت **Video recording (64-bit Windows)**:

| الإعداد | القيمة الافتراضية | ما يفعله |
| - | - | - |
| Video Codec | VP8 | يكلّف VP8 الحاسوبَ البطيء أقلّ ما يمكن؛ ويصنع VP9 ملفات أصغر مقابل وقت معالج أكثر |
| Video Width, Video Height | 1280, 720 | أكبر أبعاد الفيديو. تُحفظ نسبة الشاشة، ولا تُكبَّر النافذة الأصغر، ويُقرَّب كل ضلع نزولًا إلى مضاعف للعدد 16 |
| Video Frames Per Second | 15 | الإطارات المسجّلة في كل ثانية لعب |
| Video Bitrate Kbps | 1500 | معدّل بتات الفيديو |
| Encoder Threads | 1 | خيط واحد يكلّف اللعبة أقلّ ما يمكن |
| Use Codec Default Speed, Encoder Speed | on, 12 | عند الإطفاء: السرعة التي تضبطها (VP8 من ‎-16 إلى 16، وVP9 من ‎-9 إلى 9؛ والأعلى أسرع وأسوأ مظهرًا) |
| Encoder Below Game Priority | on | يفسح المرمّز الطريق للعبة حين يكون المعالج مشغولًا |
| Max Recording Minutes | 60 | يتوقّف التسجيل نهائيًا بعد هذا العدد من دقائق اللعب |
| Max Recording Size Mb | 1536 | يتوقّف التسجيل نهائيًا قبل أن يتجاوز ملفه هذا الحجم |
| Recordings Disk Budget Mb | 4096 | أقصى ما يجوز أن تشغله كل التسجيلات المحفوظة على الجهاز معًا |

يُنسخ الإطار ويُحجَّم ويُحوَّل على بطاقة الرسوميات ويُقرأ دون انتظارها؛ ويعمل الترميز والكتابة كلٌّ في خيط
خاص به، والإطار الذي لا يلحق يُسقَط بدل أن يوقف اللعبة. وحين ينتهي التسجيل تذكر نافذة Console ما يحويه، وكم
إطارًا أُسقط، ولماذا.

<h3 id="when-a-recording-is-uploaded">
  متى يُرفع التسجيل
</h3>

يُرفع التسجيل إلى جلسته بعد أن يكتمل ملفه **وتبدأ** الجلسة، أيّهما جاء ثانيًا. ويكتمل الملف حين:

* يبلغ **Max Recording Minutes** أو **Max Recording Size Mb**؛
* يضغط اللاعب **Upload your recording** في نموذج الملاحظات، أو تستدعي لعبتك
  `ProtokitePlaytest.StopRecordingAndSendIt()`؛
* توقفه لعبتك نهائيًا بـ`ProtokitePlaytest.StopVideoRecording()`.

يُرسل مباشرة من القرص، ولا يكون كاملًا في الذاكرة أبدًا، ولا يُعدّ مرفوعًا إلا حين يقبل التخزينُ الملفَّ نفسه.
والرفع الفاشل يُعاد مرة واحدة. والتسجيل المرفوع يُحذف من الجهاز؛ وغير المرفوع يُحفظ مع جلسته، وتذكر نافذة
Console السبب.

<Note>
  **الإغلاق لا يرفع.** لا يمكن إرسال تسجيل كامل بينما تُغلق اللعبة، فالتسجيل الجاري عند الإغلاق يُكمَل
  ويُحفظ، ويرسله التشغيل التالي. ويرسل التشغيل اللاحق كل تسجيل حفظه تشغيل سابق — رفع فاشل، أو إغلاق، أو
  انهيار — الأقدم أولًا، **حتى مع إطفاء Playtesting Enabled**، فلا يضيّع بناء النشر ما سجّله بناء اختبار لعب.
  أما التسجيل الذي لم تبدأ له أي جلسة فيُحذف، إذ لا يمكن إرساله أبدًا.
</Note>

لا تعرض على اللاعب زرًّا خاصًّا بك إلا ما دامت `ProtokitePlaytest.CanSendTheRecording` صحيحة. فهي خاطئة حين
لم تبدأ جلسة للتسجيل، والزر المضغوط حينها يوقف التسجيل ولا يرسل شيئًا:

```csharp theme={null}
if (ProtokitePlaytest.CanSendTheRecording && GUILayout.Button("Send my recording"))
    ProtokitePlaytest.StopRecordingAndSendIt();
```

<h3 id="room-on-the-players-disk">
  المساحة على قرص اللاعب
</h3>

‏**Recordings Disk Budget Mb** أقصى ما يجوز أن تشغله كل التسجيلات المحفوظة على الجهاز معًا. قبل أن يبدأ
تسجيل، تُحذف التسجيلات التي تركتها تشغيلات سابقة حتى يتّسع له المكان: مقاطع الفيديو الاختبارية أولًا، ثم
التسجيلات المنتظرة للرفع، الأقدم أولًا. والتسجيل الجاري رفعه، أو الذي تمسكه نسخة من اللعبة ما زالت تعمل، لا يُحذف
أبدًا. ولا يفسح التسجيل مكانًا إلا لما يسجّله حدّ طوله بمعدّل بتاته، مع ربع زائد (نحو 845
ميغابايت بالقيم الافتراضية)، فلا يحذف تسجيلٌ قصير تسجيلًا يتّسع بجانبه. وإن بقي أقل من ميغابايت واحد، لا
يسجّل ذلك التشغيل فيديو، ويذكر تحذيرٌ الإعدادَ الذي يجب رفعه.

والتسجيل الذي قطعه انهيار أو انقطاع كهرباء أو إنهاء قسري للعبة يُكمله التشغيل التالي بكل إطار كامل فيه، ثم
يُرفع إلى جلسته كغيره؛ أما الذي لم تبدأ جلسته قط فيُحذف.

<h2 id="heavy-analytics">
  التحليلات الثقيلة
</h2>

يرسل اختبار اللعب هذه عبر تحليلات حزمة Flock، ضمن فئة `playtest`، وتُقرأ في **Dashboards → Game Metrics**:

* ‏`performance_window` لكل عشر ثوانٍ من اللعب: الوسيط والمئين 95 والمئين 99 لزمن الإطار، و`hitches` (الإطارات
  التي استغرقت **Hitch Frame Time Ms**، أي 60 افتراضيًا، أو أكثر)، والذاكرة المستعملة وذروتها، و`map` أي المشهد
  النشط. وزمن الإطار هو الوقت الفعلي بين إطار والذي يليه، فالحركة البطيئة أو اللعبة الموقوفة تُقاس بالإطارات
  التي رآها اللاعب.
* ‏`level_loaded` لكل مشهد تحمّله اللعبة مكان الذي قبله، مع المشهد السابق و`load_seconds`، أي المدة التي أوقف
  فيها التحميلُ اللعبة.
* **أحداثك الخاصة**، على الخط الزمني نفسه وفي الفئة نفسها:

```csharp theme={null}
ProtokitePlaytest.RecordPlaytestEvent("boss_fight_started",
    new Dictionary<string, object> { { "boss", "hydra" } });
```

تجيب `RecordPlaytestEvent` بـtrue حين يوضع الحدث في الطابور، ويمكن استدعاؤها من أي خيط، ولا ترسل إلا ما دامت
التحليلات الثقيلة تعمل، وهي تبدأ بعد تحميل اختبار اللعب؛ والحدث المسجّل قبل ذلك في التشغيل يجيب بـfalse ولا
يُحفظ. ويُرفض الاسمان اللذان يرسلهما اختبار اللعب بنفسه.

تحتاج التحليلات الثقيلة إلى **Analytics Enabled** في حزمة Flock: فإن كان مطفأً لا يُقاس شيء، وتذكر نافذة
Console ذلك مرة واحدة. والأحداث أحداث Flock كغيرها، فهي تنتظر لاعبًا مسجّل الدخول وتتبع موافقة التحليلات
الخاصة بحزمة Flock. راجع [التحليلات والأحداث](/ar/guides/analytics-and-events) لترى كيف تتلاءم الفئة ولوحات
المعلومات.

<Tip>
  عند اللعب دون اتصال، تملأ ستّ نوافذ في الدقيقة طابور حزمة Flock غير المتصل (**Analytics Max Cached Events**،
  أي 1000 افتراضيًا) في أقل من ثلاث ساعات. ارفعه لاختبارات اللعب التي يلعبها مختبروك دون اتصال.
</Tip>

<h2 id="exceptions">
  الاستثناءات
</h2>

تبقى الاستثناءات من شأن حزمة Flock. فهي تلتقط استثناءات اللعبة من كل خيط، والمهام الفاشلة التي لم ينتظرها
أحد، عبر **Analytics Capture Exceptions** في **Flock → Settings → Advanced Settings** (مفعّل افتراضيًا)،
وتبلّغ عنها في **Diagnostics → Errors**. ومفتاح الاستثناءات في اختبار اللعب لا يشغّل ذلك ولا يطفئه أبدًا.

والاستثناء نفسه إذا رُمي مرة بعد مرة يصل إلى لوحة المعلومات تقريرًا واحدًا، ثم تقريرَ تكرار واحدًا يعدّ
البقية حين تُغلق نافذة التكرار (**Analytics Exception Repeat Window**، أي 60 ثانية). أما أسطر `Debug.LogError`
فليست استثناءات ولا يُبلَّغ عنها.

<h2 id="the-feedback-form">
  نموذج الملاحظات
</h2>

حين ينشر اختبار اللعب نموذج ملاحظات في Protokite، يفتحه اللاعب فوق اللعبة بـ**F9** ويملؤه. وهو مبني ممّا
نشرته — كل سؤال وتسمية ونص مساعد وخيار — فتعديل النموذج في Protokite لا يحتاج إلى بناء جديد.

<Frame caption="نموذج منشور، مرسوم من اختبار اللعب: كل سؤال، والزر الذي يرسل التسجيل.">
  <img src="https://mintcdn.com/qwacks/ByDUWjWU5EEoXzc3/images/protokite/unity/feedback-form.png?fit=max&auto=format&n=ByDUWjWU5EEoXzc3&q=85&s=049f714321c2de7029604f58c0b800b2" alt="نموذج ملاحظات لاختبار اللعب فوق لعبة في Unity، فيه تقييم واختيار وإجابات نصية وزر Upload your recording" width="1440" height="810" data-path="images/protokite/unity/feedback-form.png" />
</Frame>

| الإعداد | القيمة الافتراضية | ما يفعله |
| - | - | - |
| Feedback Form Key | F9 | يفتح النموذج ويغلقه. اضغط **Detect Key** ثم المفتاح الذي تريده، أو اختره من القائمة. و**None** تترك فتحه للعبتك |
| Pause The Game While The Form Is Open | off | يجعل مقياس الوقت 0 ما دام النموذج مفتوحًا، ويعيده عند إغلاقه |

* **يفحص Send الإجابات كما يفحصها Protokite**، ويعرض كل مشكلة بجانب سؤالها قبل إرسال أي شيء: سؤال مطلوب
  تُرك فارغًا، أو تقييم خارج النطاق، أو خيار لا يعرضه السؤال.
* **النموذج المرسَل يُحفظ على الجهاز أولًا ويُرسل من هناك**، فلا يضيّعه إغلاق اللعبة ولا انقطاع الشبكة: ما لم
  يستطع الذهاب الآن يُرسل حين تعود الشبكة، أو بعد دقيقتين، أو في تشغيل لاحق. والإرسال مجددًا في الجلسة نفسها
  يستبدل الإجابات السابقة.
* إغلاقه دون إرسال يحفظ ما كُتب لبقية التشغيل.
* يظهر **Upload your recording** ما دام هذا التشغيل يسجّل الشاشة وقد بدأت جلسته: يوقف التسجيل ويرسله الآن.
  وفتح النموذج لا يوقف التسجيل من تلقاء نفسه أبدًا.

<Warning>
  **لعبتك ما زالت تقرأ مفاتيحها وهو مفتوح.** اللعبة التي تتحرّك بـWASD أو تفتح قائمة بـEscape عليها أن تتجاهل
  مدخلاتها ما دامت `ProtokitePlaytest.IsFeedbackFormOpen` صحيحة، أو أن تفعّل **Pause The Game While The Form
  Is Open**.
</Warning>

افتحه من قائمة الإيقاف الخاصة بك، واترك ذلك الخيار خارجها حين لا ينشر اختبار اللعب أي نموذج:

```csharp theme={null}
if (ProtokitePlaytest.CanOpenFeedbackForm && GUILayout.Button("Give feedback"))
    ProtokitePlaytest.OpenFeedbackForm();
```

<h3 id="a-form-of-your-own">
  نموذج خاص بك
</h3>

تعطي `ProtokitePlaytest.FeedbackForm` واجهتك الأسئلة: لكل حقل `Id` و`Type` و`Label` و`HelpText` و`Options`،
وهل هو `Required`. أجب عنها وافحصها وأرسلها عبر الفحص والحفظ نفسيهما اللذين يمرّ بهما النموذج المدمج:

```csharp theme={null}
ProtokitePlaytestFormAnswers answers = new ProtokitePlaytestFormAnswers();
answers.SetRating("rating", 4);
answers.SetChosenOption("category", "Bug");
answers.SetText("title", "Fell through the floor");
answers.SetChecked("contact", false);
foreach (ProtokitePlaytestFormProblem problem in answers.FindProblems(ProtokitePlaytest.FeedbackForm))
    Debug.Log(problem.FieldId + ": " + problem.Message);
ProtokitePlaytest.SendFeedbackForm(answers);   // false, with a warning, when there are problems
```

قارن `Type` الخاص بالحقل بثوابت `ProtokitePlaytestFormFieldTypes` بدل كتابته، وعامل النوع الذي لا تعرفه على
أنه نص، فهكذا يقرؤه الخادم.

<Warning>
  قاعدتان يسهل أن تفوتا. **اضبط كل مربع اختيار**، مؤشَّرًا كان أم لا: فالخادم يعدّ المربع غير المؤشَّر إجابة،
  والمربع المطلوب الذي لم يُضبط قط يُعدّ ناقصًا. و**الخيار المختار يجب أن يطابق حرفًا بحرف** — فالخادم يقارن
  الخيارات مقارنة تامة، فـ`bug` ليس `Bug`.
</Warning>

<h2 id="calls-your-game-can-make">
  الاستدعاءات المتاحة للعبتك
</h2>

كلها على `ProtokitePlaytest`، في مساحة الأسماء `Protokite.Playtest`. استدعها من الخيط الرئيسي؛ وتعمل
`RecordPlaytestEvent` من أي خيط آخر أيضًا.

| الاستدعاء | يجيب عن أو يفعل |
| - | - |
| `Status`, `Describe(status)`, `Config` | هل يمكن لاختبار اللعب أن يعمل، ولماذا لا، وما الذي يشغّله |
| `IsFeatureEnabled(name)`، مع `ProtokitePlaytestFeatures.VideoRecording` / `HeavyAnalytics` / `ExceptionCapturing` | هل تعمل ميزة ما، وإجابة اللاعب محسوبة |
| `PlaytestConsent`, `PlayersConsentAnswer`, `Describe(answer)` | ما سمح اللاعب لاختبار اللعب أن يجمعه |
| `SetPlaytestConsent(choice)`, `AskForPlaytestConsent()`, `IsConsentQuestionOpen` | الإجابة من شاشاتك، وطرح السؤال مجددًا |
| `PlaytestSessionId` | جلسة Protokite للتشغيل، بعد أن تبدأ |
| `SetSteamId(id, name)` | يسمّي اللاعب بمعرّفه على Steam بدل معرّف الجهاز، قبل أن تبدأ الجلسة |
| `RecordPlaytestEvent(name, properties)` | يسجّل حدثًا من أحداث اختبار اللعب الخاصة بك |
| `IsRecordingVideo`, `StopVideoRecording()` | تسجيل التشغيل |
| `CanSendTheRecording`, `StopRecordingAndSendIt()` | إرسال التسجيل الآن |
| `CanOpenFeedbackForm`, `OpenFeedbackForm()`, `CloseFeedbackForm()`, `IsFeedbackFormOpen` | النموذج المدمج |
| `FeedbackForm`, `SendFeedbackForm(answers)` | نموذج خاص بك |
| `RecordTestVideo(seconds, out whyNot)`, `TestVideoState`, `FinishedTestVideoPath`, `TestVideoProblem` | فيديو اختباري، لا يُرفع أبدًا |

ومثال الحزمة، `Samples/PlaytestSample/ProtokitePlaytestSample.cs`، يضع كل هذه على شاشة واحدة: أضفه إلى
GameObject واضغط **Play**.

<h2 id="trying-it-in-the-editor">
  التجربة في المحرّر
</h2>

في النصف السفلي من نافذة الإعداد، **Protokite → Playtest → Setup Checks And Test Video**، كل ما يلزم للاختبار.
وفي **Play Mode** تبدو هكذا:

<Frame caption="نافذة الإعداد في Play Mode: الفحوص، وإجابة هذا الجهاز، والفيديو الاختباري، والاختبار الذاتي المباشر.">
  <img src="https://mintcdn.com/qwacks/ByDUWjWU5EEoXzc3/images/protokite/unity/setup-window-play-mode.png?fit=max&auto=format&n=ByDUWjWU5EEoXzc3&q=85&s=0c5f2d70dc2f1c2030953097cc9e1688" alt="نافذة إعداد Protokite Playtest أثناء Play Mode، مع أقسام While testing وTest video وLive self-test" width="540" height="354" data-path="images/protokite/unity/setup-window-play-mode.png" />
</Frame>

* ‏**Forget This Machine's Answer** يجعل Play التالي يطرح سؤال الموافقة مجددًا.
* ‏**Open Feedback Form** يفتح النموذج كما يفتحه مفتاحه.
* ‏**Record Test Video** يسجّل نافذة Game للثواني التي تضبطها، بإعدادات الفيديو و**دون حاجة إلى اختبار لعب**:
  فلا بأس أن يكون اختبار اللعب مطفأً وألا يكون أحد مسجّل الدخول. يُحفظ الفيديو الاختباري في
  `ProtokitePlaytest/Recordings/TestVideos/` داخل مجلد البيانات الدائمة للعبة، ولا يُرفع أبدًا، وتعرض النافذة
  أين ذهب. ويُرفض ما دام تسجيل اختبار اللعب نفسه جاريًا، ويفسح له الطريق. وككل فيديو، لا يُسجَّل إلا على
  Windows بنواة 64 بت.
* ‏**Run Live Self-Test** يفحص اختبار اللعب كله مقابل Protokite الحقيقي لديك (أدناه).

ويمكن للعبة أن تسجّل فيديو اختباريًا بنفسها، من زر «سجّل مقطعًا» في قائمة تصحيح مثلًا:

```csharp theme={null}
if (!ProtokitePlaytest.RecordTestVideo(30.0, out string whyNot))
    Debug.Log("No test video: " + whyNot);
```

<h2 id="checking-a-playtest-build">
  فحص بناء اختبار لعب
</h2>

يفحص الاختبار الذاتي المباشر كل ما يفعله بناء اختبار اللعب مقابل Protokite الحقيقي لديك دفعة واحدة، ويسجّل
سطرًا لكل خطوة وعدًّا في النهاية. شغّله في Play Mode بـ**Run Live Self-Test**، أو من الشيفرة في المحرّر أو في
بناء تطوير (بناء النشر يرفضه):

```csharp theme={null}
ProtokitePlaytestSelfTestReport report = await ProtokitePlaytestSelfTest.RunAsync();
Debug.Log($"{report.Passed} passed, {report.Failed} failed, {report.Skipped} skipped");
```

كل فحص يجاوره طلب يجب على Protokite أن يرفضه: مفتاح واجهة برمجية خاطئ، ومفتاح مفقود، وإصدار لا اختبار لعب
مرتبط به، وبدء جلسة لا يسمّي لاعبًا، ونموذج ينقصه جواب مطلوب أو يختار خيارًا ليس في القائمة، ونموذج ورابط رفع
ونهاية لجلسة غير موجودة. فالفحص الذي لا ينجح إلا دائمًا لا يميّز بناءً سليمًا من معطوب، ولذلك لا يُحتسب الرفض
إلا برمز HTTP الخاص به، وفي النموذج بالسؤال الذي يسمّيه.

* **سجّل دخول لاعب أولًا، وأجب عن سؤال الموافقة.** فهو لا يسجّل دخول أحد؛ والخطوات التي تحتاج إلى جلسة أو إجابة
  تُتخطّى مع ذكر السبب.
* **شغّله في Play أو تشغيل خاص به.** فهو يوقف تسجيل هذا التشغيل ليرفعه، ويرسل نموذجًا مملوءًا يستبدل ما أرسله
  اللاعب في هذا التشغيل، ويرمي استثناءين عمدًا — فإن كان **Error Pause** مفعّلًا في Console يتوقّف Play عندهما
  حتى تستأنفه.
* **يترك أثرًا في لوحات معلوماتك:** نموذجًا مملوءًا واحدًا في الجلسة، وحدث `playtest_self_test` واحدًا،
  واستثناءً واحدًا مرميًّا مرتين ليُحتسب تكراره، وتسجيل التشغيل. وكلٌّ منها يحمل معرّف التشغيل (`report.RunId`)،
  لتجدها في Flock وProtokite.
* **تُتخطّى الخطوة مع ذكر السبب** حين لا يشغّل اختبار اللعب ميزتها، وحين لا يكون في البناء مرمّز فيديو (كل منصّة
  سوى Windows بنواة 64 بت)، وفي اختبار اللعب المغلق ما لم تسمِّ واحدًا: مرّر معرّف Game Version لاختبار لعب مغلق
  إلى `RunAsync`، أو اكتبه في **Closed Playtest Version ID** في النافذة.

<h2 id="what-stays-on-the-players-machine">
  ما يبقى على جهاز اللاعب
</h2>

تحت `ProtokitePlaytest/` في مجلد البيانات الدائمة للعبة (`Application.persistentDataPath`):

| المسار | يحوي | حتى |
| - | - | - |
| `Recordings/Playtest/` | تسجيلات اختبار اللعب، كلٌّ في مجلد مع الجلسة التي ينتمي إليها | تُرفع، أو تُحذف لإفساح المكان، والأقدم آخرًا |
| `Recordings/TestVideos/` | مقاطع الفيديو الاختبارية | تُحذف لإفساح المكان، والأقدم أولًا |
| `FeedbackForms/` | إجابات لم يُستطع إرسالها بعد، ولا مفتاح واجهة برمجية فيها | تُرسل، أو يرفضها الخادم |
| `device_id.txt` | معرّف الجهاز لهذا التثبيت، حين لا يُضبط معرّف Steam | يبقى، ليظل التثبيت الواحد لاعبًا واحدًا |
| `playtest_consent.json` | ما سمح اللاعب لاختبار اللعب أن يجمعه | يبقى، ليُسأل مرة واحدة لا في كل تشغيل |

<h2 id="platforms">
  المنصّات
</h2>

كل ما في هذه الصفحة يعمل حيثما تعمل حزمة Flock، عدا الفيديو.

**يُسجَّل الفيديو على Windows بنواة 64 بت فقط**، في المحرّر وفي المشغّلات، مع Mono وIL2CPP على السواء. ويعمل
على Direct3D 11 و12 وVulkan وOpenGL، في Built-in Render Pipeline وURP، وبالألوان Linear وGamma، ويحتاج إلى
بطاقة رسوميات تشغّل compute shaders.

**«لا فيديو على هذه المنصّة»** يعني ذلك بالضبط ولا شيء أكثر: البناء لأي منصّة أخرى — macOS أو Linux أو
الهواتف أو WebGL أو Windows بنواة 32 بت أو ARM64 — يترك مرمّز الفيديو خارجه، ويذكر مرة واحدة في
Console أنه لا يسجّل فيديو، ويشغّل كل شيء آخر: الجلسة، وسؤال الموافقة، والتحليلات الثقيلة، والاستثناءات، ونموذج
الملاحظات. وتنبّهك نافذة الإعداد مسبقًا: «Players built for … record no video». واللعبة التي لا ترسم شيئًا (بناء خادم، أو
لعبة بدأت بـ`-nographics` أو `-batchmode`) لا تسجّل شيئًا كذلك، ولا تستطيع عرض سؤال الموافقة: أجب عنه من الشيفرة
بـ`SetPlaytestConsent`، أو أطفئ السؤال.

**على WebGL** تُنسخ ملفات اختبار اللعب — إجابة الموافقة، ومعرّف الجهاز، ونماذج الملاحظات المنتظرة للإرسال — إلى
تخزين المتصفّح بعد كل تغيير، فتكون موجودة في زيارة اللاعب التالية حيثما يحفظ المتصفّح بيانات الموقع. والنافذة
الخاصة تنساها حين تُغلق.
