שחקנים באזורי זמן שונים, ניתוקי אינטרנט, אובדן התקדמות — נקודות כאב טיפוסיות במשחקי מולטיפלייר אסינכרוניים. כיצד מבטיחים סנכרון נכון של מהלכים כאשר משתמש יכול להיות במצב לא מקוון במשך שעות? הפתרון שלנו הוא event sourcing עם גרסאות מצב וארכיטקטורת offline-first. במשך למעלה מ-10 שנים, יישמנו מולטיפלייר אסינכרוני ביותר מ-30 פרויקטים עם קהל כולל של יותר מ-2 מיליון משתמשים. בקשו פיתוח מולטיפלייר אסינכרוני כדי שהשחקנים שלכם יוכלו לבצע מהלכים בכל זמן ללא אובדן התקדמות.
מולטיפלייר אסינכרוני: החלטות ארכיטקטוניות מרכזיות
אחסון מצב וגרסאות
כל מהלך הוא עסקת מסד נתונים. מבנה הרשומה: game_id, move_number, player_id, action_payload (JSON), timestamp, resulting_state_hash. ה-hash של המצב לאחר כל מהלך מאפשר זיהוי סטיות. Event Sourcing שומר את ההיסטוריה המלאה של הפעולות: ניתן לשחזר כל רגע במשחק, ליישם replay, ביקורת, וחזרה לאחור במקרה של באגים. בפרויקט עם 500,000 התקנות, גישה זו צמצמה את זמן תיקון הבאגים ב-60%.
סנכרון באמצעות Polling ו-WebSocket
בתחילת סשן, הלקוח מבצע GET /game/{id}/state ומקבל את המצב עם version. לאחר מכן שתי אפשרויות התראה: polling כל 30-60 שניות או WebSocket/SSE כאשר האפליקציה פתוחה + push כאשר היא סגורה. Polling מעמיס על השרת ב-40% יותר. עדיף: כאשר האפליקציה פתוחה — WebSocket, כאשר סגורה — FCM/APNs push. עם קבלת push, הלקוח מבצע GET /game/{id}/state?since_version={lastKnown} ומקבל רק שינויים. הגישה ההיברידית מפחיתה את התעבורה פי 3.
למה Event Sourcing עדיף למולטיפלייר אסינכרוני?
Event sourcing שומר את כל המהלכים כרצף של אירועים. זה מאפשר:
- חזרה לאחור במשחק לכל נקודה;
- יישום replay לצופים (ghosting);
- ביצוע ביקורת איזון וזיהוי רמאות.
בניגוד לאחסון רק את המצב הנוכחי, event sourcing מאפשר תיקון קל של שגיאות בלוגיקת המשחק ללא אובדן נתונים. בפועל, אנו משתמשים ב-PostgreSQL עם event store או Firebase Firestore עם אוסף moves. השוו בין שתי הגישות:
| קריטריון | Event Sourcing | אחסון מצב נוכחי |
|---|---|---|
| שחזור כל נקודה | כן | לא |
| Replay | קל | קשה |
| ביקורת | מסלול מלא | מוגבל |
| נפח נתונים | גבוה | נמוך |
| מורכבות | בינונית | נמוכה |
כיצד אנו פותרים קונפליקטים במהלכים בו-זמניים?
קונפליקטים מתרחשים אם שני שחקנים שלחו מהלך באותו זמן (לדוגמה, עקב באג בלקוח). השרת מקבל רק את הראשון לפי חותמת זמן ומחזיר שגיאה לשני עם המצב הנוכחי. לתרחישים מורכבים יותר (מהלכים בו-זמניים במצבים שאינם מבוססי תורות) אנו משתמשים ב-Conflict-free Replicated Data Types (CRDT), אך למשחקי תורות אסינכרוניים, חותמת זמן + hash מספיקים. זמן השהיית התראה ממוצע כאשר האפליקציה פתוחה הוא 2–5 שניות.
מנגנון סנכרון מהלכים למולטיפלייר אסינכרוני
לסנכרון אמין, אנו משתמשים בשילוב של WebSocket (סשן פעיל) והתראות push (אפליקציה סגורה). אם החיבור נופל, הלקוח עובר ל-polling עם backoff אקספוננציאלי: עיכוב ראשון של 1 שנייה, לאחר מכן 2, 4, 8, וכו' עד 60 שניות. זה מבטיח מסירת מהלכים גם עם ניתוק זמני.
UX של Offline-first
המשתמש מבצע מהלך ללא אינטרנט — המהלך נשמר מקומית ונשלח כאשר החיבור משוחזר. באנדרואיד — WorkManager עם NetworkType.CONNECTED: המשימה תבוצע כאשר האינטרנט יופיע, גם אם האפליקציה סגורה.
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val submitMoveRequest = OneTimeWorkRequestBuilder<SubmitMoveWorker>()
.setConstraints(constraints)
.setInputData(workDataOf("move_payload" to moveJson))
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS)
.build()
WorkManager.getInstance(context).enqueue(submitMoveRequest)ב-iOS — val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() val submitMoveRequest = OneTimeWorkRequestBuilder<SubmitMoveWorker>() .setConstraints(constraints) .setInputData(workDataOf("move_payload" to moveJson)) .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30, TimeUnit.SECONDS) .build() WorkManager.getInstance(context).enqueue(submitMoveRequest) עם BGProcessingTask. המהלך נשמר ב-Core Data, והמשימה נשלחת בהזדמנות המוקדמת ביותר. לפי סטטיסטיקות מהפרויקטים שלנו, 95% מהמהלכים נמסרים תוך 30 שניות לאחר שחזור החיבור.
השוואת שיטות סנכרון
| שיטה | זמן השהיה | עומס שרת | מצב לא מקוון |
|---|---|---|---|
| Polling (30-60 שניות) | בינוני | גבוה | לא נתמך |
| WebSocket | מינימלי | בינוני | דורש חיבור מחדש |
| WebSocket + Push | מינימלי | בינוני | נתמך |
מה כלול בעבודה
- ארכיטקטורת צד שרת (DB, API, WebSocket)
- SDK ללקוח ל-iOS ואנדרואיד (Swift/Kotlin)
- לוגיקת מהלכים, גרסאות, טיפול בקונפליקטים
- תור לא מקוון וסנכרון (WorkManager, BGProcessingTask)
- התראות push (FCM/APNs)
- תיעוד API ואינטגרציה
- בדיקות (יחידה, אינטגרציה, עומס)
- תמיכה לאחר השקה (חודשיים)
תהליך העבודה
- ניתוח מכניקת המשחק ודרישות
- עיצוב ארכיטקטורה (סכמת DB, חוזי API)
- יישום לוגיקת צד שרת (NestJS/PostgreSQL או Firebase)
- אינטגרציית קוד לקוח (Swift/Kotlin)
- בדיקת סנכרון ותרחישי מצב לא מקוון
- פריסה וניטור
לוח זמנים והערכת עלות
מערכת אסינכרונית בסיסית ל-2 שחקנים עם event sourcing, offline-first והתראות push: 2-4 שבועות. העלות מחושבת באופן אישי לאחר ניתוח מכניקת המשחק. תצורות מורכבות יותר (יותר שחקנים, CRDT) — מ-3 עד 6 שבועות. הלקוחות שלנו חוסכים 30% עד 50% מזמן הפיתוח בהשוואה למולטיפלייר סינכרוני הודות לרכיבים לשימוש חוזר ותבניות מוכנות.
אמון וניסיון
הצוות שלנו פיתח למעלה מ-30 משחקי מובייל עם מולטיפלייר, כולל פרויקטים עם 500,000+ התקנות. אנו מבטיחים עמידה בלוחות זמנים ושיתוף פעולה שקוף. אנו עובדים turnkey: מהרעיון ועד פרסום בחנות. קבלו ייעוץ — ספרו לנו על מכניקת המשחק שלכם, ואנו נציע פתרון אופטימלי. צרו קשר כדי לדון בפרויקט שלכם ולקבל הערכת עלות אישית.







