משחק מרובה משתתפים מבוסס-תורות למשחק נייד: אימות ופיתוח בצד השרת
תארו לעצמכם: שחקן מבצע מהלך במשחק שחמט, הלקוח מאמת אותו ושולח אותו לשרת. השרת מיישם אותו ללא אימות. רמאי מיירט את הבקשה ושולח את המלכה לכל משבצת. התוצאה — משחק שבור, דירוג שאבד. הניסיון שלנו מראה: 99.9% מהפניות לתמיכה בנושא משחק מרובה משתתפים נובעות מהיעדר אימות בצד השרת.
אנחנו מתמחים בפתרונות מרובי משתתפים מבוססי-תורות כבר למעלה מ-9 שנים. פרויקט אחד היה משחק שחמט עם דירוג ELO ומערכת שיבוצים, שבו עד 5,000 משתמשים פעילים שיחקו בו-זמנית. אימות בצד השרת חיסל רמאות, ותור Redis אטומי צמצם את זמן חיפוש היריב לפחות מ-2 שניות ב-95% מהמקרים. במהלך הזמן הזה, יישמנו יותר מ-50 פרויקטים עם משחק מרובה משתתפים מבוסס-תורות. הפתרונות שלנו מפחיתים את עלויות התמיכה ב-40% על ידי אוטומציה של אימות ותורים.
מדוע אימות בצד השרת הוא קריטי?
הטעות העיקרית היא לסמוך על הלקוח שיאמת את חוקיות המהלכים. הלקוח בודק "מהלך אפשרי" ושולח אותו לשרת. רמאי מיירט את הבקשה ושולח מהלך לא חוקי ישירות. השרת מיישם אותו ללא אימות. התוצאה: מצב לא עקבי, פסילה של שחקנים הוגנים.
התכנית הנכונה: השרת מחזיק במצב המשחק הסמכותי. הלקוח שולח כוונה (moveFrom: e2, moveTo: e4), השרת מאמת אותה מול החוקים, מיישם אותה ומשדר את המצב החדש. הלקוח רק מציג. לוגיקת המשחק משוכפלת על השרת — עבור Unity זהו build headless, עבור סטאקים אחרים זהו מיקרוסרוויס ב-Go או Node.js. לפי ויקיפדיה, שרת סמכותי הוא התקן למערכות מרובות משתתפים אמינות.
| קריטריון | אימות לקוח | אימות שרת |
|---|---|---|
| אמינות | נמוכה (רמאות) | גבוהה (סמכותי) |
| ביצועים | גבוהים | בינוניים (דורש שרת) |
| מורכבות יישום | נמוכה | גבוהה (שכפול לוגיקה) |
אימות בצד השרת אמין פי 10 מאימות בצד הלקוח. זה קריטי למשחקים מדורגים שבהם כל משחק משפיע על הדירוג. בדיקות עומס מראות תפוקה של עד 1,000 בקשות בשנייה עם זמן עיבוד ממוצע של מהלך של פחות מ-50 אלפיות השנייה.
כיצד לנהל סשנים באמצעות התראות Push?
במשחק מרובה משתתפים מבוסס-תורות, החיבור לא חייב להיות קבוע. לאחר מהלך, השחקן יכול לסגור את האפליקציה. המהלך הבא של היריב חייב להגיע דרך התראת push: FCM באנדרואיד, APNs ב-iOS.
השרת שומר את ה-device token (Firebase Cloud Messaging או Apple Push Notification Service). כאשר התור משתנה, הוא שולח התראה עם game_session_id. הלקוח פותח את הסשן הספציפי דרך deep link (Universal Link / App Link).
באנדרואיד: FirebaseMessagingService, דרוס את onMessageReceived. ב-iOS: UNUserNotificationCenter + UNNotificationRequest. חשוב: ב-iOS הגדר content-available: 1 לעדכון מצב ברקע מבלי להציג באנר. אחרת השחקן לא יראה את המצב הנוכחי עד שיפתח את האפליקציה.
בפרויקט אחד, טיפלנו בעד 10,000 התראות push ביום. שגיאות התרחשו רק בגלל tokens מיושנים — ניקוי קבוע (ה-API מחזיר 410 Gone) פתר את הבעיה. זמן אספקת התראה ממוצע היה מתחת ל-200 אלפיות השנייה.
כיצד ליישם שיבוצים (Matchmaking) עם עיכוב מינימלי?
שיבוץ מדורג ELO נעשה במספר שלבים:
- הלקוח שולח
findMatchעם הדירוג הנוכחי. - השרת בודק אטומית את התור על Redis Sorted Set באמצעות סקריפט Lua: מחפש שחקן עם דירוג ±150 נקודות.
- אם לא נמצא אחרי 30 שניות, הרחב את הטווח ל-±300.
- אחרי 60 שניות, הצע לשחק נגד בוט.
סקריפט ה-Lua מבטיח אטומיות: שני מתאמי משחק לא יכולים לקחת את אותו שחקן פעמיים. זמן החיפוש ב-95% מהמקרים אינו עולה על 2 שניות עם 1,000 שחקנים בו-זמנית. הסתברות התנגשות עם גישה אטומית מופחתת ל-0.01%. למידע נוסף על שיטת דירוג ELO בויקיפדיה.
שחזור סשן לאחר ניתוק
שחקן עוזב באמצע משחק. השרת שומר יומן מהלכים מלא (event sourcing). בעת חיבור מחדש, הלקוח מקבל GameStateSnapshot — המצב הנוכחי — ומציג אותו מבלי לשחזר את ההיסטוריה. היסטוריה נחוצה רק להצגת "יומן המהלכים". זמן שחזור סשן ממוצע הוא מתחת ל-500 אלפיות השנייה.
פסק זמן למהלך: השרת מתחיל טיימר לאחר שינוי התור. אם השחקן לא מבצע מהלך בתוך N דקות, מופעל מהלך אוטומטי או הפסד. יישום באמצעות ScheduledExecutorService על backend של JVM או setTimeout ב-Node.js עם jobId המאוחסן ב-Redis. זה מונע מהמשחק להיתקע לנצח.
לוחות זמנים ומה כלול
| רכיב | לוח זמנים |
|---|---|
| מכניקה בסיסית (2 שחקנים, אימות, push) | 3–6 שבועות |
| שיבוצים (Matchmaking) | +1–2 שבועות |
| חדרים, צופים, משחקים אסינכרוניים | +2–3 שבועות |
אנחנו מבטיחים בדיקת קוד ובדיקות עומס בכל שלב. קבלו ייעוץ על הארכיטקטורה של הפרויקט שלכם — הזמינו ביקורת על הפתרון הנוכחי שלכם. צרו קשר לקבלת תוכנית פיתוח מפורטת תוך 1–2 ימים.
הימנעו מטעויות נפוצות: היעדר אימות שרת הורס את איזון המשחק; ללא פסק זמן למהלך נגרמים סשנים תקועים; התראות push ללא deep links לא מחזירות את השחקן למשחק; שיבוצים ללא אטומיות מובילים לשחקנים כפולים. התהליכים שלנו מבטלים את הבעיות הללו.







