إعداد الحزمة والمنصة
استخدم الحزمة داخل builds الحديثة، أو اعتمد على النسخة العامة التي تحقنها المنصة تلقائيًا في ألعاب ZIP المرفوعة. لا تبنِ طبقات wrapper خاصة فوق SDK.
1
# Preferred for packaged games4
# GameHub-hosted ZIP uploads5
# No manual install or script tag is required. The platform injects:6
<script src="/sdk/gamehub-sdk.js" data-game-slug="your-game-slug"></script>8
# Local standalone testing only9
<script src="https://gamesbeta.ksaa.gov.sa/sdk/gamehub-sdk.js"></script>مهم: SDK يعمل كنسخة singleton ويبدأ التهيئة تلقائيًا. على المطور انتظار ready() ثم استخدام الدوال العامة مباشرة.
تهيئة وقت التشغيل
اقرأ سياق التشغيل القادم من GameHub: بيانات اللعبة، المصادقة والجلسة، بيانات الدوري والمباراة، غرفة اللعب الجماعي، peer id، اللاعب الحالي، والمشاركين.
TypeScript / JavaScript — package usage17 أسطر
1
// Bundled app: React, Angular, Vue, Phaser, Vite, Webpack, etc.2
import GameHubSDK from "gamehub-sdk";4
const sdk = GameHubSDK || window.GameHubSDK;6
await sdk.ready(); // no init() call is required8
const bootstrap = sdk.getBootstrap();11
gameSlug: bootstrap.gameSlug,12
leagueId: bootstrap.leagueId,13
matchId: bootstrap.matchId,14
peerId: bootstrap.peerId,15
currentPlayer: bootstrap.currentPlayer,16
players: bootstrap.players,
JavaScript — injected global10 أسطر
1
// If your game does not use a bundler, use the injected global.2
await window.GameHubSDK.ready();4
const sdk = window.GameHubSDK;5
const bootstrap = sdk.getBootstrap();7
if (bootstrap.matchId) {8
const match = await sdk.getMatchContext();9
renderParticipants(match.participant1, match.participant2);يرجع BootstrapData
gameSlugstring | null— slug اللعبة الحالي.
leagueId / gameId / matchIdstring | null— سياق الدوري والمباراة عندما يتم تشغيل اللعبة من الدوري.
matchMatchContext | null— سياق المباراة بعد تحميله من getMatchContext.
playersBootstrapPlayer[] | null— قائمة اللاعبين التي يرسلها المضيف، تشمل peerId و role و displayName و avatar.
currentPlayerBootstrapPlayer | null— اللاعب الحالي المطابق لقيمة peerId.
roomBootstrapRoom | null— ملخص غرفة اللعب الجماعي عند توفره.
لا يوجد init
SDK يبدأ تلقائيًا عند تحميله. استخدم ready() كحاجز انتظار فقط.
سياق غني
getBootstrap() يحتوي بيانات اللعبة والدوري والمباراة والغرفة واللاعب الحالي.
حزمة واحدة
استخدم gamehub-sdk داخل اللعبة مباشرة ولا تنسخ ملفات SDK أو تبني wrapper منفصل.
الجلسات والنتائج
أرسل نتائج الألعاب المستقلة مع بيانات اختيارية. في غرف اللعب الجماعي يمنع SDK تعدد الإرسال بحيث يكتب المضيف النتيجة فقط.
1
// Submit a normal score when a standalone game ends.2
async function submitFinalScore(score, gameState) {3
await GameHubSDK.ready();5
const result = await GameHubSDK.submitScore(score, {6
mode: gameState.mode, // "single" | "two_players_local" | "two_players_online"7
correctAnswers: gameState.correctAnswers,8
totalQuestions: gameState.totalQuestions,9
durationMs: gameState.durationMs,10
level: gameState.level,13
if (result.skipped) return; // online guest/viewer: host will submit14
if (!result.success) showSubmitError(result.error);معاملات submitScore
| المعامل | النوع | مطلوب | القيمة الافتراضية | الوصف |
|---|
score | number | مطلوب | — | النتيجة النهائية، ويجب أن تكون عددًا صحيحًا غير سالب. |
meta.mode | "single" | "two_players_local" | "two_players_online" | اختياري | — | وضع اللعب المستخدم لتجميع لوحة الصدارة. |
meta.correctAnswers | number | اختياري | — | عدد الإجابات الصحيحة لتحليل مكافحة الغش. |
meta.totalQuestions | number | اختياري | — | إجمالي الأسئلة المعروضة للاعب. |
meta.durationMs | number | اختياري | — | الوقت المستغرق بالمللي ثانية، ويستخدم في التحقق من مكافحة الغش. |
meta.roomCode | string | اختياري | — | كود غرفة اللعب الجماعي إن وجد. |
يرجع Promise<object>
successboolean— هل نجح استدعاء SDK.
acceptedboolean— هل قبل الخادم النتيجة بعد اجتياز مكافحة الغش.
timeTakennumber— مدة اللعب المحسوبة من الخادم بالمللي ثانية.
errorstring?— رسالة الخطأ عندما تكون success بقيمة false.
skippedboolean?— تظهر عندما يتجاهل SDK الإرسال لأن اللاعب ليس المضيف في غرفة جماعية.
مباريات الدوري
عند تشغيل اللعبة من مباراة دوري، يوفر SDK سياق المباراة ويتيح للمضيف إرسال الفائز ونتائج المشاركين وتفاصيل النتيجة.
JavaScript — league-match.js22 أسطر
1
await GameHubSDK.ready();3
const bootstrap = GameHubSDK.getBootstrap();4
const match = await GameHubSDK.getMatchContext();6
if (bootstrap.matchId && match) {7
showSideA(match.participant1.name, match.participant1.members);8
showSideB(match.participant2.name, match.participant2.members);11
// Submit the bracket result. Use the participantId from match context.12
await GameHubSDK.submitMatchResult({13
winnerId: match.participant1.participantId,15
[match.participant1.participantId]: 12,16
[match.participant2.participantId]: 8,20
reason: "normal_finish",حقول سياق المباراة
يرجع MatchContext | null
idstring— معرّف مباراة الدوري الحالية.
leagueIdstring— معرّف الدوري الذي تنتمي إليه المباراة.
gameIdstring— معرّف اللعبة المطلوبة لهذه المباراة.
participant1{ participantId, name, members[] }— الفريق أو اللاعب الأول، مع أسماء الأعضاء عند توفرها.
participant2{ participantId, name, members[] }— الفريق أو اللاعب الثاني، مع أسماء الأعضاء عند توفرها.
statusstring— حالة المباراة في جدول الدوري.
معاملات submitMatchResult
| المعامل | النوع | مطلوب | القيمة الافتراضية | الوصف |
|---|
winnerId | string | اختياري | — | معرّف participantId للفائز كما هو موجود في match.participant1 أو match.participant2. |
scores | Record<string, number> | اختياري | — | خريطة نتائج المشاركين، حيث يكون المفتاح هو participantId والقيمة هي النتيجة. |
reason | string | اختياري | — | سبب إنهاء المباراة مثل normal_finish أو timeout أو forfeit. |
details | Record<string, unknown> | اختياري | — | أي تفاصيل إضافية تحتاجها لوحة الإدارة أو التحليلات. |
في اللعب الجماعي، استدع submitMatchResult من المضيف فقط. إذا استدعاه لاعب ضيف أو مشاهد فسيعيد SDK نتيجة skipped بدون إرسال طلب للشبكة.
الأسئلة
اجلب الأسئلة المنشورة مباشرة من المنصة. يستخدم SDK قيمة gameId عندما يوفرها المضيف، وإلا يستخدم slug اللعبة.
JavaScript — questions.js26 أسطر
1
// All parameters are optional.2
async function loadQuestions() {3
await GameHubSDK.ready();5
// Returns an array of question objects directly (empty array on error)6
const questions = await GameHubSDK.getQuestions({7
difficulty: "medium", // "easy" | "medium" | "hard" (optional)8
category: "science", // any string (optional)9
limit: 20, // 1–100, defaults to 100 (optional)12
console.log(questions.length); // number of questions returned14
// Each question shape:20
// answer: string | number21
// explanation: string22
// difficulty: "easy" | "medium" | "hard"خيارات getQuestions
| المعامل | النوع | مطلوب | القيمة الافتراضية | الوصف |
|---|
difficulty | "easy" | "medium" | "hard" | اختياري | — | تصفية الأسئلة حسب مستوى الصعوبة. |
category | string | اختياري | — | تصفية الأسئلة حسب وسم التصنيف مثل science أو history. |
limit | number | اختياري | 100 | الحد الأقصى لعدد الأسئلة المعادة من 1 إلى 100. |
يرجع Promise<Question[]>
[n].idstring— معرّف السؤال الفريد.
[n].promptstring— نص السؤال.
[n].choicesstring[]— خيارات الإجابة، وتكون فارغة للأسئلة المفتوحة.
[n].answerstring | number— قيمة الإجابة الصحيحة أو رقمها.
[n].difficulty"easy" | "medium" | "hard"— مستوى الصعوبة.
[n].categorystring— وسم التصنيف.
[n].pointsnumber— قيمة النقاط.
شكل كائن السؤال
| الحقل | النوع | الوصف |
|---|
id | string | معرّف السؤال الفريد. |
type | string | نوع السؤال مثل multiple_choice. |
prompt | string | نص السؤال المعروض للاعب. |
choices | string[] | خيارات الإجابة، وتكون فارغة للأسئلة المفتوحة. |
answer | string | number | قيمة الإجابة الصحيحة أو رقمها. |
explanation | string | شرح اختياري يظهر بعد الإجابة. |
difficulty | "easy" | "medium" | "hard" | مستوى صعوبة السؤال. |
category | string | وسم التصنيف المستخدم للتصفية. |
points | number | قيمة النقاط المخصصة لهذا السؤال. |
آلية العمل: يتم تقديم الأسئلة من مجموعة MongoDB مرتبطة باللعبة. يتم إرجاع الأسئلة المنشورة فقط، ويتم اختيار النتائج عشوائيًا في كل طلب. published
اللعب الجماعي بصلاحية المضيف
غرف فورية بروابط مشاركة ونموذج يعتمد المضيف كمصدر موثوق. يرسل الضيوف النوايا، ويتحقق المضيف، ويبث الحالة النهائية، ثم يرسل النتيجة.
المضيف هو المصدر
العشوائية، ترتيب الأدوار، التحقق، وإنهاء المباراة يجب أن تتم من المضيف ثم تبث عبر broadcast.
الضيف يرسل نية
استخدم send من اللاعبين لإرسال النقرات أو الاختيارات، ولا تطبق النتيجة النهائية حتى يبثها المضيف.
حماية النتيجة
submitScore و submitMatchResult يعملان من المضيف فقط داخل الغرفة. غير المضيف يحصل على skipped.
1
// Host creates and shares a room2
await GameHubSDK.ready();4
const room = await GameHubSDK.multiplayer.createRoom({ maxPlayers: 2 });5
if (!room.success) throw new Error(room.error);7
// Share room.shareUrl or room.roomCode with other players8
console.log("Room code:", room.roomCode);9
console.log("Share link:", room.shareUrl);1
// Player joins via share link or room code2
await GameHubSDK.ready();4
await GameHubSDK.multiplayer.joinRoomByLink(window.location.href);6
await GameHubSDK.multiplayer.joinRoom("ROOM_CODE", { displayName: "Player 2" });8
await GameHubSDK.multiplayer.joinRoom("ROOM_CODE", { asViewer: true });10
// Listen for events from other players11
GameHubSDK.multiplayer.on("message", (msg) => {12
console.log(msg.eventName, msg.payload);15
// Send player intent to the room16
GameHubSDK.multiplayer.send("player_move", { x: 2, y: 1 });1
await GameHubSDK.ready();3
const mp = GameHubSDK.multiplayer;5
// Host owns randomness and authoritative state.6
if (!mp.isConnected()) {7
await mp.createRoom({ maxPlayers: 2 });10
mp.on("peer_joined", () => {11
if (!mp.isHost()) return;12
const seed = crypto.randomUUID();13
mp.broadcast("match_start", { seed, turn: 0 });16
mp.on("match_start", ({ payload }) => {17
buildBoardFromSeed(payload.seed);20
// Players send intent. They do not mutate final state locally.21
function onCellClick(cellId) {22
mp.send("cell_click", { cellId });25
// Host validates and broadcasts the result.26
mp.on("cell_click", ({ fromPeerId, payload }) => {27
if (!mp.isHost()) return;28
const result = resolveMove(fromPeerId, payload.cellId);29
mp.broadcast("cell_resolved", result);32
// Everyone applies the same resolved state.33
mp.on("cell_resolved", ({ payload }) => {34
applyResolvedMove(payload);36
if (payload.isFinal && mp.isHost()) {37
GameHubSDK.submitMatchResult(payload.matchResult);خيارات createRoom
| المعامل | النوع | مطلوب | القيمة الافتراضية | الوصف |
|---|
maxPlayers | number | اختياري | 4 | الحد الأقصى للاعبين المسموح بهم في الغرفة من 2 إلى 16. |
passcode | string | اختياري | — | رمز مرور اختياري من 4 إلى 32 حرفًا لتقييد الدخول. |
خيارات joinRoom
| المعامل | النوع | مطلوب | القيمة الافتراضية | الوصف |
|---|
passcode | string | اختياري | — | رمز المرور المطلوب إذا أنشأ المضيف الغرفة برمز. |
displayName | string | اختياري | — | الاسم المعروض لهذا اللاعب في قائمة اللاعبين. |
asViewer | boolean | اختياري | false | يجعل اللاعب للعرض فقط: لا send ولا broadcast ولا إرسال نتائج. |
أحداث النظام
| الحدث | البيانات | الوصف |
|---|
connected | { roomCode, peerId } | تم فتح اتصال WebSocket. |
disconnected | { roomCode } | تم إغلاق اتصال WebSocket وقد تبدأ إعادة الاتصال. |
reconnecting | { attempts, delayMs } | SDK ينتظر قبل محاولة إعادة الاتصال التالية. |
room_joined | { peerId, role, peers[] } | يتم إطلاقه عند الانضمام إلى الغرفة بنجاح. |
peer_joined | { peerId, ... } | اتصل لاعب جديد بالغرفة. |
peer_left | { peerId } | انقطع اتصال لاعب أو غادر الغرفة. |
host_changed | { peerId } | تم نقل دور المضيف إلى لاعب جديد. |
room_closed | { reason } | أغلق المضيف الغرفة وتم قطع اتصال جميع اللاعبين. |
room_left | { reason } | غادر اللاعب المحلي الغرفة أو أغلقت الغرفة عليه. |
error | { code } | رفض الخادم رسالة مثل RATE_LIMITED أو MESSAGE_TOO_LARGE. |
message | { eventName, payload, fromPeerId, seq } | حدث مخصص أرسله لاعب آخر عبر send(). |
مرجع API
قائمة كاملة بدوال window.GameHubSDK.
الدوال الأساسية
| الدالة | يرجع | الوصف |
|---|
ready() | Promise<boolean> | ينتهي عند اكتمال تهيئة SDK. من الآمن انتظاره قبل أي استدعاء آخر. |
isInitialized() | boolean | تكون true بعد أن يحل SDK سياق اللعبة وينهي التهيئة التلقائية. |
isAuthenticated() | boolean | تكون true عندما يملك SDK رمز مصادقة من المنصة الحاضنة. |
startSession() | Promise<{success, token}> | يبدأ جلسة لعب جديدة. يتم استدعاؤه تلقائيًا من submitScore إذا لم يتم استدعاؤه يدويًا. |
playAgain() | Promise<{success, token}> | يشير إلى بدء جولة أخرى مع إعادة استخدام رمز الجلسة الحالي. |
submitScore(score, meta?) | Promise<{success, accepted, timeTaken, skipped?}> | يرسل النتيجة النهائية للاعب مع بيانات اختيارية. في غرف اللعب الجماعي تعيد الأطراف غير المضيفة skipped بدون كتابة. |
submitMatchScore(score, meta?) | Promise<{success, accepted, timeTaken}> | يرسل نتيجة لمباراة دوري تم تشغيلها من الإدارة ويربط matchId الحالي بمحاولة الجلسة المحفوظة. |
submitMatchResult(result) | Promise<{success, accepted, skipped?}> | يرسل نتيجة مباراة في جدول الدوري مع نقاط المشاركين وبيانات الفائز. |
getMatchContext() | Promise<MatchContext | null> | يجلب ويخزن سياق مباراة الدوري الحالية، بما يشمل أسماء المشاركين وأعضاء الفرق. |
getQuestions(options?) | Promise<Question[]> | يجلب أسئلة هذه اللعبة. يعيد مصفوفة فارغة عند الخطأ. جميع الخيارات اختيارية. |
getBootstrap() | BootstrapData | يعيد حالة SDK وإعدادات المضيف: اللعبة، المصادقة، الدوري، المباراة، الغرفة، اللاعبين، واللاعب الحالي. |
getGameSlug() | string | null | يعيد slug اللعبة الذي تمت تهيئة SDK به. |
hasActiveSession() | boolean | تكون true إذا كان رمز الجلسة موجودًا حاليًا في الذاكرة. |
notifyComplete(data?) | void | يرسل رسالة GAME_COMPLETE إلى الإطار الأب. |
requestClose() | void | يطلب من المنصة إغلاق اللعبة أو الانتقال خارجها. |
logEvent(name, data?) | void | يرسل حدث تحليلات مسمى إلى المنصة الحاضنة. |
isInIframe() | boolean | تكون true عندما تكون اللعبة مضمنة داخل iframe. |
دوال اللعب الجماعي
| الدالة | يرجع | الوصف |
|---|
multiplayer.createRoom(options?) | Promise<{success, roomCode, shareUrl, peerId, roomToken}> | ينشئ غرفة لعب جماعي جديدة ويصبح المستدعي هو المضيف. |
multiplayer.joinRoom(roomCode) | Promise<{success, roomCode, peerId}> | ينضم إلى غرفة موجودة باستخدام الكود المختصر. |
multiplayer.joinRoomByLink(url) | Promise<{success, roomCode, peerId}> | ينضم إلى غرفة باستخدام رابط المشاركة الكامل. |
multiplayer.leaveRoom(reason?) | Promise<void> | يغادر الغرفة الحالية. عند إغلاق المضيف للغرفة يتم إرسال room_closed لجميع اللاعبين. |
multiplayer.send(eventName, payload?) | boolean | يبث حدثًا مخصصًا إلى جميع اللاعبين داخل الغرفة. |
multiplayer.broadcast(eventName, payload?) | boolean | إرسال خاص بالمضيف لتحديثات الحالة الموثوقة. الضيوف والمشاهدون يعيدون false. |
multiplayer.on(eventName, handler) | () => void | يشترك في حدث معين ويعيد دالة لإلغاء الاشتراك. |
multiplayer.getRoom() | {roomCode, role, peerId, ...} | null | يعيد حالة الغرفة الحالية. |
multiplayer.getPeers() | Peer[] | يعيد قائمة بجميع اللاعبين المتصلين في الغرفة. |
multiplayer.isConnected() | boolean | تكون true إذا كان اتصال WebSocket مفتوحًا حاليًا. |
multiplayer.getRole() | string | null | يعيد دور الطرف المحلي: host أو player أو viewer أو null. |
multiplayer.isHost() | boolean | تكون true عندما يكون هذا الطرف هو مضيف الغرفة الموثوق. |
multiplayer.isViewer() | boolean | تكون true عندما ينضم هذا الطرف بخيار asViewer ويكون للعرض فقط. |
multiplayer.selfId() | string | null | يعيد peerId المحلي لاستخدامه في تصفية الرسائل المرتدة من نفس اللاعب. |