توسعه بازی‌های چند نفره با فریمورک Colyseus

Developing Multiplayer Games with the Colyseus Framework

مقدمه

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

Colyseus یک فریمورک متن‌ باز برای ساخت بازی‌های چندنفره و Real-time است که عمدتاً روی Node.js و TypeScript تمرکز دارد. این ابزار به شما کمک می‌کند بدون پیاده‌سازی دستی تمام پیچیدگی‌های WebSocket، معماری اتاق‌ها، همگام‌سازی state و مدیریت بازیکنان، بک‌اند یک بازی آنلاین را توسعه دهید.

به زبان ساده، Colyseus زیرساخت لازم برای ساخت سرور بازی‌های آنلاین را فراهم می‌کند، از بازی‌ های ساده مثل دوز و کارت گرفته تا بازی‌ های رقابتی، همکاری‌ محور و اپلیکیشن‌های Real-time.

 

Colyseus چیست؟

Colyseus یک فریمورک Multiplayer Game Server برای Node.js است که توسعه‌دهنده با استفاده از آن می‌تواند سرورهای بازی چندنفره مبتنی بر WebSocket بسازد.

مفهوم اصلی در Colyseus، یک Room یا اتاق است. هر اتاق می‌تواند نماینده یک مسابقه، یک لابی، یک میز بازی، یک کانال گفتگو، یک جلسه همکاری یا حتی یک فضای مجازی باشد.

برای مثال:

- هر مسابقه فوتبال آنلاین یک Room مستقل است.
- هر میز بازی Poker یک Room جداگانه محسوب می‌شود.
- هر مسابقه Battle Royale می‌تواند یک اتاق باشد.
- یک لابی برای پیدا کردن بازیکن نیز یک Room است.
- یک جلسه وایت‌بورد یا همکاری تیمی بلادرنگ نیز می‌تواند به‌شکل اتاق پیاده‌سازی شود.

Colyseus ارتباط بین کلاینت و سرور را از طریق WebSocket مدیریت می‌کند و مکانیزم مناسبی برای ارسال پیام، نگهداری state و ارسال تغییرات state به کاربران متصل فراهم می‌سازد.

 

 چرا برای بازی‌های آنلاین به یک فریمورک مانند Colyseus نیاز داریم؟

اگر بخواهید یک بازی چندنفره را فقط با WebSocket خام توسعه دهید، باید بسیاری از موضوعات مهم را خودتان طراحی و پیاده‌سازی کنید:

- مدیریت اتصال و قطع اتصال کاربران
- ساخت، حذف و جستجوی اتاق‌ها
- تخصیص بازیکن به مسابقه مناسب
- مدیریت ظرفیت اتاق
- ارسال پیام‌های Real-time
- همگام‌سازی وضعیت بازی
- جلوگیری از تداخل دستورات بازیکنان
- مدیریت زمان بازی و Game Loop
- بازیابی وضعیت پس از اتصال مجدد
- کنترل اعتبار پیام‌های دریافتی
- مقیاس‌پذیری سرور
- انتقال کاربران میان چند instance سرور

Colyseus بسیاری از این مسئولیت‌ها را با ساختاری استاندارد و قابل‌توسعه در اختیار شما قرار می‌دهد. بنابراین تیم توسعه می‌تواند روی قوانین بازی، تجربه کاربری و منطق کسب‌وکار تمرکز بیشتری داشته باشد.

 

مهم‌ترین ویژگی‌های Colyseus

 1. معماری مبتنی بر Room

هسته اصلی Colyseus بر پایه مفهوم Room طراحی شده است.

هر Room یک فضای مستقل برای کاربران متصل است که می‌تواند شامل موارد زیر باشد:

- بازیکنان یک مسابقه
- وضعیت فعلی بازی
- تایمرها
- قوانین اتاق
- ظرفیت بازیکنان
- پیام‌های اختصاصی
- داده‌های موقت هر مسابقه
- وضعیت برنده، بازنده یا پایان بازی

برای نمونه، در یک بازی دوز، هر اتاق ممکن است فقط دو بازیکن داشته باشد. در یک بازی نقش‌آفرینی آنلاین، هر اتاق می‌تواند شامل ده‌ها یا صدها بازیکن باشد.

نمونه اولیه از تعریف یک اتاق در TypeScript:


import { Room, Client } from "colyseus";
export class GameRoom extends Room {
 maxClients = 2;
 onCreate(options: unknown) {
   console.log("Game room created", options);
 }
 onJoin(client: Client, options: unknown) {
   console.log(${client.sessionId} joined the room);
 }
 onLeave(client: Client) {
   console.log(${client.sessionId} left the room);
 }
 onDispose() {
   console.log("Game room disposed");
 }
}

در این ساختار، چرخه عمر اتاق با متدهایی مانند onCreate، onJoin، onLeave و onDispose مدیریت می‌شود.

 

 2. همگام‌سازی خودکار State

یکی از بهترین قابلیت‌های Colyseus، سیستم State Synchronization است.

در بازی‌های چندنفره، سرور باید وضعیت فعلی بازی را به کلاینت‌ها منتقل کند. برای مثال:

- موقعیت بازیکنان
- امتیاز هر کاربر
- وضعیت کارت‌ها
- زمان باقی‌مانده
- وضعیت نقشه
- نوبت بازیکن فعلی
- سلامت کاراکتر
- آیتم‌های موجود در محیط

اگر این داده‌ها به‌صورت کامل و در هر لحظه برای همه کاربران ارسال شوند، مصرف شبکه به‌شدت افزایش می‌یابد. Colyseus با استفاده از سیستم Schema، فقط تغییرات state را به کلاینت‌ها ارسال می‌کند.

این موضوع به‌خصوص برای بازی‌هایی که تعداد پیام بالا یا بازیکنان متعدد دارند، اهمیت زیادی دارد.

نمونه‌ای از تعریف state:


import { Schema, type, MapSchema } from "@colyseus/schema";
class Player extends Schema {
 @type("number")
 x = 0;
 @type("number")
 y = 0;
 @type("number")
 score = 0;
}
export class GameState extends Schema {
 @type({ map: Player })
 players = new MapSchema<Player>();
 @type("string")
 currentTurn = "";
}

در این مثال:

- هر بازیکن موقعیت x و y دارد.
- امتیاز بازیکن در score ذخیره می‌شود.
- بازیکنان در MapSchema نگهداری می‌شوند.
- تغییرات این داده‌ها به‌صورت بهینه برای کلاینت‌ها ارسال می‌شود.

 

 3. ارتباط دوطرفه مبتنی بر WebSocket

Colyseus برای ارتباط لحظه‌ای میان کلاینت و سرور از WebSocket استفاده می‌کند.

برخلاف HTTP که در آن کلاینت باید برای دریافت اطلاعات درخواست ارسال کند، در WebSocket ارتباط دائمی‌تری میان سرور و کاربر برقرار می‌شود. بنابراین سرور می‌تواند بدون نیاز به درخواست جدید از سمت کلاینت، تغییرات بازی را بلافاصله ارسال کند.

این قابلیت برای موارد زیر ضروری است:

- حرکت کاراکترها
- به‌روزرسانی آنی امتیاز
- نمایش پیام‌های حین بازی
- شروع هم‌زمان مسابقه
- اعلام برد یا باخت
- نمایش حرکات بازیکنان دیگر
- مدیریت تایمر
- اجرای مسابقات زنده

 

 4. مدیریت Lifecycle اتاق و بازیکنان

در Colyseus برای هر اتاق یک چرخه عمر مشخص وجود دارد. شما می‌توانید در هر مرحله منطق اختصاصی خودتان را اجرا کنید.

مهم‌ترین lifecycle hookها عبارت‌اند از:

متدکاربرد
onCreate   هنگام ایجاد اتاق اجرا می‌شود 
onAuthبرای اعتبارسنجی و احراز هویت کاربر استفاده می‌شود 
onJoin   هنگام ورود بازیکن به اتاق اجرا می‌شود 
onLeave   هنگام خروج یا قطع اتصال کاربر اجرا می‌شود 
onDispose   هنگام حذف کامل اتاق اجرا می‌شود 

 

   


 

نمونه اعتبارسنجی کاربر با JWT:


import { Room, Client } from "colyseus";
export class SecureGameRoom extends Room {
 async onAuth(client: Client, options: { token?: string }) {
   if (!options.token) {
     throw new Error("Unauthorized: token is required");
   }
   // اعتبارسنجی JWT یا بررسی کاربر در دیتابیس
   return {
     userId: "user-123",
     username: "player-one",
   };
 }
 onJoin(client: Client) {
   console.log("Authenticated user joined:", client.auth.userId);
 }
}

این الگو برای بازی‌هایی که دارای حساب کاربری، کیف پول، پرداخت، رتبه‌بندی یا سیستم جایزه هستند اهمیت بالایی دارد.

 

 5. ارسال و دریافت پیام‌های سفارشی

همه داده‌ها نباید در State ذخیره شوند. برخی اطلاعات فقط یک رویداد لحظه‌ای هستند و نیازی به نگهداری در وضعیت دائمی بازی ندارند.

مثال‌ها:

- بازیکن دکمه آماده‌بودن را زده است.
- کاربر درخواست حرکت داده است.
- کاربر پیام چت ارسال کرده است.
- بازیکن درخواست استفاده از یک آیتم را داده است.
- کاربر قصد شلیک دارد.
- بازیکن درخواست خروج از مسابقه را ثبت کرده است.

در Colyseus می‌توانید برای این موارد از پیام‌های سفارشی استفاده کنید.

 دریافت پیام در سرور


import { Room, Client } from "colyseus";
export class GameRoom extends Room {
 onCreate() {
   this.onMessage("move", (client: Client, message: { x: number; y: number }) => {
     console.log("Move received:", client.sessionId, message);
     // اعتبارسنجی حرکت
     // اعمال حرکت روی state
   });
 }
}

 ارسال پیام از کلاینت


room.send("move", {
 x: 10,
 y: 5,
});

 ارسال پیام از سرور به کاربران


this.broadcast("game-over", {
 winnerId: "player-1",
 reason: "score-limit-reached",
});

 

معماری Colyseus چگونه کار می‌کند؟

در معماری استاندارد Colyseus، چند بخش اصلی وجود دارد:


Client Application
     │
     │ WebSocket
     ▼
Colyseus Server
     │
     ├── Matchmaking
     ├── Room Management
     ├── State Synchronization
     ├── Authentication
     └── Game Logic
            │
            ▼
      Database / Cache / Queue

 

 اجزای مهم این معماری

 

 1. کلاینت یا Client

کلاینت می‌تواند با فناوری‌های مختلف توسعه داده شود:

- JavaScript
- TypeScript
- React
- Vue
- Angular
- Phaser
- PixiJS
- Unity
- Godot
- Cocos Creator
- Defold

کلاینت مسئول نمایش بازی، دریافت ورودی کاربر، ارسال فرمان‌ها و نمایش وضعیت دریافتی از سرور است.

 

 2. سرور Colyseus

سرور وظایف اصلی زیر را انجام می‌دهد:

- ساخت اتاق‌ها
- اعتبارسنجی کاربران
- اجرای قوانین بازی
- نگهداری state
- ارسال state به کلاینت‌ها
- پردازش پیام‌ها
- اجرای Game Loop
- مدیریت قطع اتصال و اتصال مجدد

 

 3. دیتابیس

دیتابیس معمولاً برای ذخیره داده‌های دائمی استفاده می‌شود، نه state لحظه‌ای هر بازی.

برای مثال:

- اطلاعات کاربران
- تاریخچه مسابقات
- امتیازات
- رتبه‌بندی
- پرداخت‌ها
- کیف پول
- آیتم‌های خریداری‌شده
- آمار بازی
- نتایج مسابقات

برای این بخش می‌توان از ابزارهایی مثل PostgreSQL، MongoDB، MySQL یا Redis استفاده کرد.

 

 

 4. Redis

Redis در پروژه‌های Colyseus می‌تواند کاربردهای مهمی داشته باشد:

- نگهداری sessionها
- cache کردن اطلاعات
- ذخیره داده‌های کوتاه‌مدت
- مدیریت presence
- هماهنگی میان چند سرور
- مقیاس‌پذیری افقی
- صف‌بندی داده‌ها یا eventها

 

 

 Matchmaking در Colyseus

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

برای مثال، وقتی یک کاربر روی دکمه «شروع بازی» کلیک می‌کند، سیستم باید بررسی کند:

- آیا اتاق خالی وجود دارد؟
- آیا اتاقی با ظرفیت مناسب وجود دارد؟
- آیا بازیکنان سطح مشابه دارند؟
- آیا نوع بازی موردنظر کاربر با اتاق سازگار است؟
- آیا منطقه جغرافیایی یا ping بازیکن مناسب است؟
- آیا بازی دارای رمز یا تنظیمات خاص است؟

در Colyseus متدهایی مانند joinOrCreate برای چنین سناریوهایی کاربرد دارند.


const room = await client.joinOrCreate("game_room", {
 gameMode: "ranked",
 region: "me",
});

در سمت سرور می‌توانید اتاق‌ها را بر اساس metadata یا تنظیمات ورودی فیلتر کنید.

نمونه:


import { Room } from "colyseus";
export class RankedRoom extends Room {
 maxClients = 2;
 onCreate(options: { gameMode?: string }) {
   this.setMetadata({
     gameMode: options.gameMode ?? "casual",
   });
 }
}

 

 

 Game Loop در Colyseus

بسیاری از بازی‌ها به یک حلقه اجرای مداوم نیاز دارند که معمولاً Game Loop نام دارد.

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

- موقعیت کاراکترها
- برخوردها
- سرعت و شتاب
- وضعیت گلوله‌ها
- سلامت بازیکنان
- پایان زمان
- شرایط برد و باخت

در Colyseus می‌توانید از setSimulationInterval استفاده کنید.

 


onCreate() {
 this.setSimulationInterval((deltaTime) => {
   this.updateGame(deltaTime);
 });
}
private updateGame(deltaTime: number) {
 // منطق اصلی بازی
}

نکته مهم این است که بهتر است نرخ Game Loop را متناسب با نوع بازی تعیین کنید.

 

نوع پروژهنرخ پیشنهادی 
بازی نوبتیبدون نیاز به loop سریع 
 بازی کارت بر اساس event 
 بازی استراتژیحدود 5 تا 20 بار در ثانیه 
 بازی اکشن دوبعدیحدود 20 تا 30 بار در ثانیه
بازی سریع رقابتیحدود 30 تا 60 بار در ثانیه 

 
 

هرچه نرخ بروزرسانی بالاتر باشد، مصرف CPU، شبکه و هزینه سرور نیز افزایش پیدا می‌کند.

 

چه پروژه‌هایی برای Colyseus مناسب هستند؟

Colyseus فقط برای بازی نیست. هر پروژه‌ای که به ارتباط Real-time، state مشترک و مدیریت چند کاربر در یک فضای مستقل نیاز داشته باشد، می‌تواند از آن استفاده کند.

 1. بازی‌های نوبتی

Colyseus برای بازی‌های نوبتی گزینه بسیار مناسبی است، زیرا نیاز به همگام‌سازی سریع در سطح فریم ندارند و منطق اصلی روی سرور قابل‌کنترل است.

نمونه‌ها:

- دوز
- شطرنج
- تخته‌نرد
- بازی‌های کارتی
- Poker
- Uno
- حکم
- بازی‌های رومیزی
- بازی‌های کلمه‌ای
- بازی‌های حدس کلمه

ویژگی‌های موردنیاز:

- مدیریت نوبت
- جلوگیری از حرکت غیرمجاز
- تایمر نوبت
- ثبت نتیجه بازی
- اتصال مجدد کاربر
- تاریخچه حرکت‌ها
- رتبه‌بندی بازیکنان

 

 2. بازی‌های Casual و اجتماعی

بازی‌های ساده و اجتماعی معمولاً با معماری Room سازگاری زیادی دارند.

نمونه‌ها:

- مسابقات سوال و جواب
- بازی حدس تصویر
- بازی‌های آموزشی
- اتاق‌های رقابتی کوتاه
- بازی‌های چندنفره دوستانه
- بازی‌های مهمانی یا Party Games

برای مثال، در یک بازی کوییز آنلاین، هر اتاق می‌تواند شامل موارد زیر باشد:

- لیست شرکت‌کنندگان
- سوال فعلی
- گزینه‌ها
- زمان باقی‌مانده
- پاسخ‌های ثبت‌شده
- امتیاز کاربران
- وضعیت پایان بازی

 

 3. بازی‌های اکشن دوبعدی

برای بازی‌های دوبعدی سریع نیز می‌توان از Colyseus استفاده کرد؛ به‌خصوص اگر بازی با Phaser، PixiJS یا Unity توسعه داده شده باشد.

نمونه‌ها:

- بازی تیراندازی دوبعدی
- بازی ماشین‌سواری آرکید
- بازی مبارزه‌ای ساده
- بازی‌های Top-down
- بازی‌های فوتبال رومیزی
- بازی‌های بقا دوبعدی
- بازی‌های Arena

در این نوع پروژه‌ها باید به موارد زیر توجه ویژه داشته باشید:

- Server Authoritative Architecture
- نرخ بروزرسانی سرور
- بهینه‌سازی پیام‌ها
- پیش‌بینی حرکت در کلاینت
- جبران تأخیر شبکه
- اعتبارسنجی input کاربران
- جلوگیری از تقلب

 

 4. بازی‌های نقش‌آفرینی و RPG

اگر بازی شما دارای اتاق‌ها، نقشه‌ها، بازیکنان متعدد و تعاملات گروهی است، Colyseus می‌تواند گزینه خوبی باشد.

نمونه‌ها:

- بازی RPG آنلاین سبک
- بازی‌های Dungeon چندنفره
- بازی‌های همکاری‌محور
- بازی‌های MMO-lite
- بازی‌های جهان کوچک با اتاق‌های متعدد

در این سناریو، هر نقشه یا dungeon می‌تواند یک Room باشد. بازیکنان بر اساس موقعیت، مرحله یا گروه خود به اتاق مناسب منتقل می‌شوند.

 

5. چت و اپلیکیشن‌های Real-time

هرچند Colyseus یک فریمورک بازی است، اما ساختار آن برای برخی اپلیکیشن‌های Real-time نیز کاربرد دارد.

نمونه‌ها:

- چت گروهی اتاق‌محور
- اتاق‌های گفت‌وگوی صوتی یا متنی
- سیستم حضور کاربران
- داشبوردهای زنده
- سیستم مانیتورینگ بلادرنگ
- لایو کوییز
- اتاق‌های همکاری تیمی
- وایت‌بورد مشترک
- ابزارهای Collaborative Editing

بااین‌حال، اگر پروژه شما صرفاً یک سیستم چت است، ابزارهایی مانند Socket.IO یا SignalR ممکن است ساده‌تر و مناسب‌تر باشند. نقطه قوت اصلی Colyseus زمانی دیده می‌شود که علاوه‌بر ارتباط لحظه‌ای، به Room Management و Shared State ساختاریافته نیاز داشته باشید.

 

 مزایای استفاده از Colyseus

 

 1. توسعه سریع‌تر بازی‌های چندنفره

Colyseus بسیاری از نیازهای تکراری بازی‌های آنلاین را آماده در اختیار شما قرار می‌دهد. در نتیجه لازم نیست از صفر سیستم اتاق، sync state و lifecycle بازیکنان را طراحی کنید.

 2. مناسب برای TypeScript

اگر بک‌اند شما با Node.js و TypeScript توسعه پیدا می‌کند، Colyseus تجربه توسعه خوبی ارائه می‌دهد.

مزیت‌های مهم TypeScript در این پروژه‌ها:

- type safety
- کاهش خطاهای runtime
- خوانایی بیشتر کد
- DTOهای مشخص برای پیام‌ها
- refactor ساده‌تر
- autocomplete بهتر در WebStorm و VS Code

نمونه DTO برای حرکت بازیکن:


export interface MovePlayerDto {
 x: number;
 y: number;
 sequence: number;
}

و استفاده در room:


this.onMessage("move", (client, message: MovePlayerDto) => {
 if (
   typeof message.x !== "number" ||
   typeof message.y !== "number" ||
   typeof message.sequence !== "number"
 ) {
   client.send("error", {
     code: "INVALID_MOVE_PAYLOAD",
     message: "Invalid move request.",
   });
   return;
 }
 // پردازش حرکت معتبر
});

 

 

 3. معماری Server Authoritative

در معماری Server Authoritative، سرور مرجع نهایی حقیقت است. یعنی کلاینت فقط درخواست یا input می‌فرستد و سرور تصمیم می‌گیرد که آن درخواست معتبر است یا خیر.

این معماری برای جلوگیری از تقلب اهمیت زیادی دارد.

برای مثال، در یک بازی آنلاین نباید به کلاینت اعتماد کنید که اعلام کند:

- من برنده شدم.
- امتیاز من 1000 شد.
- من به نقطه X منتقل شدم.
- من به بازیکن دیگر آسیب زدم.
- من کارت خاصی دارم.
- من پول یا جایزه دریافت کردم.

کلاینت باید فقط فرمان یا input ارسال کند و سرور باید تمام قوانین را بررسی کند.

نمونه نادرست:


this.onMessage("update-score", (client, message) => {
 player.score = message.score;
});

نمونه بهتر:


this.onMessage("answer-question", (client, message: { answerId: string }) => {
 const player = this.state.players.get(client.sessionId);
 if (!player) {
   return;
 }
 const isCorrect = this.validateAnswer(message.answerId);
 if (isCorrect) {
   player.score += 10;
 }
});

 

 

 4. ارسال بهینه تغییرات state

ارسال فقط تغییرات state باعث کاهش مصرف شبکه و بهبود تجربه کاربران می‌شود.

این موضوع در پروژه‌هایی با ویژگی‌های زیر بسیار مهم است:

- تعداد بازیکن زیاد
- state پیچیده
- اتصال اینترنت ضعیف‌تر
- کاربران موبایل
- بازی‌هایی با حرکت دائمی
- سرورهای با هزینه محدود

 

 5. پشتیبانی از بازیابی اتصال

قطع اینترنت کاربر یک اتفاق رایج است؛ به‌خصوص در بازی‌های موبایل. Colyseus ابزارهایی برای مدیریت Reconnection در اختیار توسعه‌دهنده می‌گذارد.

نمونه:


async onLeave(client: Client, consented: boolean) {
 if (consented) {
   return;
 }
 try {
   await this.allowReconnection(client, 20);
 } catch {
   this.removePlayer(client.sessionId);
 }
}

در این مثال، اگر کاربر به‌صورت ناگهانی قطع شود، تا 20 ثانیه فرصت اتصال مجدد دارد.

 

6. قابلیت مقیاس‌پذیری

در مرحله ابتدایی، ممکن است یک instance از سرور برای پروژه کافی باشد. اما با افزایش کاربران، نیاز به اجرای چند instance از سرور خواهید داشت.

Colyseus برای معماری‌های مقیاس‌پذیر طراحی شده و می‌تواند در کنار ابزارهایی مانند موارد زیر استفاده شود:

- Redis
- Docker
- Kubernetes
- NGINX
- Traefik
- PostgreSQL
- Prometheus
- Grafana

برای پروژه‌های جدی، بهتر است از ابتدا به این موضوعات فکر کنید:

- sticky session
- load balancing
- room persistence
- Redis presence
- monitoring
- log aggregation
- error tracking
- autoscaling

 

 

 مقایسه Colyseus با Socket.IO

بسیاری از توسعه‌دهندگان در ابتدا میان Colyseus و Socket.IO مردد هستند.

واقعیت این است که این دو ابزار دقیقاً برای یک هدف مشابه طراحی نشده‌اند.

 

ویژگی Colyseus.io Socket.IO
هدف اصلیبازی و اپلیکیشن‌های statefulارتباط Real-time عمومی
مدیریت اتاقتخصصی و بازی‌محوروجود دارد اما ساده‌تر
همگام‌سازی stateداخلی و بهینهباید دستی پیاده‌سازی شود
matchmaking آماده و ساختار یافتهباید دستی توسعه یابد
game loopپشتیبانی مناسبباید دستی طراحی شود
state schemaدارد ندارد 
مناسب برای چت سادهممکن است بیش‌ازحد باشد بسیار مناسب
مناسب برای بازی آنلاینبسیار مناسبنیازمند توسعه بیشتر 

 

 

 چه زمانی Socket.IO بهتر است؟

اگر پروژه شما شامل موارد زیر است، Socket.IO گزینه ساده‌تر و مناسب‌تری است:

- چت ساده
- اعلان لحظه‌ای
- داشبورد مدیریتی زنده
- سیستم حضور کاربران
- انتقال eventهای ساده
- اپلیکیشن Real-time بدون state پیچیده

 

 چه زمانی Colyseus بهتر است؟

اگر پروژه شما شامل موارد زیر است، Colyseus انتخاب قدرتمندتری است:

- بازی چندنفره
- اتاق مسابقه
- سیستم matchmaking
- state مشترک
- بازی نوبتی
- رتبه‌بندی و مسابقات
- قوانین سمت سرور
- بازی اکشن با sync state
- مدیریت reconnect بازیکنان

 

 مقایسه Colyseus با SignalR

SignalR یک ابزار محبوب در اکوسیستم .NET است که برای ساخت اپلیکیشن‌های Real-time کاربرد دارد. اگر بک‌اند شما با ASP.NET Core توسعه یافته، احتمالاً SignalR گزینه آشنایی برای شماست.

 

ویژگی SignalR Colyseus 
کوسیستم اصلیASP.NET Core و C#Node.js و TypeScript
تمرکز اصلی ارتباط بلادرنگ عمومیبازی چندنفره
Room lifecycle نیازمند طراحی بیشتر بسیار مناسب برای بازی
state synchronization عمدتاً دستیداخلی 
matchmakingباید توسعه داده شوددارد 
game loopباید دستی پیاده‌ سازی شود مناسب 
بازی‌های چندنفرهقابل‌ استفاده اما با توسعه بیشتر انتخاب تخصصی‌تر
پنل و داشبورد زندهانتخاب عالیقابل‌استفاده 


 

اگر پروژه اصلی شما ASP.NET Core است اما قصد ساخت یک ماژول بازی آنلاین دارید، می‌توانید معماری ترکیبی داشته باشید:

- سرویس اصلی با ASP.NET Core
- مدیریت کاربران، پرداخت و دیتابیس با ASP.NET Core
- سرور بازی با Colyseus
- ارتباط میان سرویس‌ها با REST API، gRPC یا message broker

 

 

 شروع پروژه با Colyseus

 

 پیش‌نیازها

برای شروع توسعه با Colyseus معمولاً به موارد زیر نیاز دارید:

- Node.js
- npm یا pnpm
- TypeScript
- دانش پایه WebSocket
- آشنایی با مفاهیم بازی چندنفره
- یک کلاینت مانند Phaser، React یا Unity

 ایجاد پروژه

نمونه‌ای از راه‌اندازی اولیه:

bash
npm create colyseus-app@latest my-colyseus-game
 

سپس وارد پوشه پروژه شوید:

bash
cd my-colyseus-game
 

وابستگی‌ها را نصب کنید:

bash
npm install
 

و پروژه را اجرا کنید:

bash
npm run start
 

ساختار پروژه معمولاً شامل بخش‌هایی مانند زیر است:


src/
├── app.config.ts
├── index.ts
├── rooms/
│   ├── GameRoom.ts
│   └── schema/
│       └── GameState.ts
└── services/

 

نمونه یک اتاق بازی ساده

در این مثال، یک اتاق ساده ایجاد می‌کنیم که بازیکنان به آن متصل می‌شوند و می‌توانند موقعیت خود را تغییر دهند.

 تعریف Player


import { Schema, type } from "@colyseus/schema";
export class Player extends Schema {
 @type("number")
 x = 0;
 @type("number")
 y = 0;
 @type("number")
 score = 0;
}

 تعریف GameState


import { Schema, type, MapSchema } from "@colyseus/schema";
import { Player } from "./Player";
export class GameState extends Schema {
 @type({ map: Player })
 players = new MapSchema<Player>();
}

پیاده‌سازی GameRoom


import { Client, Room } from "colyseus";
import { GameState } from "./schema/GameState";
import { Player } from "./schema/Player";
interface MovePlayerDto {
 x: number;
 y: number;
}
export class GameRoom extends Room<GameState> {
 maxClients = 4;
 onCreate() {
   this.setState(new GameState());
   this.onMessage("move", (client, message: MovePlayerDto) => {
     this.handleMove(client, message);
   });
 }
 onJoin(client: Client) {
   const player = new Player();
   player.x = 0;
   player.y = 0;
   player.score = 0;
   this.state.players.set(client.sessionId, player);
 }
 onLeave(client: Client) {
   this.state.players.delete(client.sessionId);
 }
 private handleMove(client: Client, message: MovePlayerDto) {
   const player = this.state.players.get(client.sessionId);
   if (!player) {
     client.send("error", {
       code: "PLAYER_NOT_FOUND",
       message: "Player state was not found.",
     });
     return;
   }
   if (
     typeof message.x !== "number" ||
     typeof message.y !== "number"
   ) {
     client.send("error", {
       code: "INVALID_MOVE",
       message: "Move coordinates are invalid.",
     });
     return;
   }
   const maxCoordinate = 1000;
   player.x = Math.max(-maxCoordinate, Math.min(message.x, maxCoordinate));
   player.y = Math.max(-maxCoordinate, Math.min(message.y, maxCoordinate));
 }
}

در این نمونه:

- حداکثر چهار بازیکن می‌توانند وارد اتاق شوند.
- برای هر بازیکن یک Player در state ایجاد می‌شود.
- بازیکن پیام move ارسال می‌کند.
- سرور ورودی را اعتبارسنجی می‌کند.
- مختصات بازیکن در سرور به‌روزرسانی می‌شود.
- تغییرات state برای کاربران دیگر sync می‌شود.

 

 اتصال کلاینت به Colyseus

نمونه اتصال با JavaScript یا TypeScript:


import { Client } from "colyseus.js";
const client = new Client("ws://localhost:2567");
const room = await client.joinOrCreate("game_room", {
 gameMode: "casual",
});
room.onStateChange((state) => {
 console.log("New game state:", state);
});
room.send("move", {
 x: 250,
 y: 100,
});

در پروژه‌های واقعی، بهتر است اتصال، خطا، reconnect و lifecycle اتاق را در یک service مستقل مدیریت کنید.

برای مثال در React، می‌توانید یک ColyseusGameService یا یک hook اختصاصی بسازید تا مسئولیت‌های زیر را مدیریت کند:

- اتصال به سرور
- join کردن room
- خروج از room
- ارسال پیام
- دریافت تغییرات state
- مدیریت reconnect
- نمایش خطاها
- پاک‌سازی connection هنگام unmount شدن صفحه

 

 نکات مهم امنیتی در پروژه‌های Colyseus

امنیت در بازی‌های آنلاین بسیار مهم است؛ به‌خصوص اگر پروژه دارای رتبه‌بندی، جایزه، پرداخت، کیف پول یا رقابت باشد.

 

 1. هرگز به کلاینت اعتماد نکنید

کلاینت قابل تغییر است. کاربر می‌تواند پیام‌های جعلی ارسال کند، کد را دستکاری کند یا درخواست‌های غیرمجاز بسازد.

سرور باید موارد زیر را بررسی کند:

- آیا بازیکن واقعاً عضو اتاق است؟
- آیا نوبت بازیکن است؟
- آیا حرکت مجاز است؟
- آیا کاربر به منابع لازم دسترسی دارد؟
- آیا زمان انجام عملیات معتبر است؟
- آیا کاربر محدودیت‌ها را رعایت کرده است؟
- آیا payload معتبر است؟

 

 2. اعتبارسنجی پیام‌ها

برای اعتبارسنجی داده‌های دریافتی از کلاینت، می‌توانید از ابزارهایی مانند موارد زیر استفاده کنید:

- Zod
- class-validator
- Joi
- Yup

نمونه با Zod:


import { z } from "zod";
const movePlayerSchema = z.object({
 x: z.number().min(-1000).max(1000),
 y: z.number().min(-1000).max(1000),
 sequence: z.number().int().nonnegative(),
});
this.onMessage("move", (client, payload) => {
 const validationResult = movePlayerSchema.safeParse(payload);
 if (!validationResult.success) {
   client.send("error", {
     code: "INVALID_MOVE_PAYLOAD",
     message: "Move payload validation failed.",
   });
   return;
 }
 const moveDto = validationResult.data;
 // پردازش درخواست معتبر
});

 

 

 3. استفاده از JWT

برای پروژه‌هایی که حساب کاربری دارند، بهتر است کاربر قبل از ورود به اتاق با JWT احراز هویت شود.

اطلاعاتی که بهتر است در token یا session موجود باشد:

- userId
- username
- role
- walletId
- subscriptionStatus
- permissions

اما اطلاعات حساس مانند موجودی قابل‌اعتماد کیف پول نباید فقط از token خوانده شود؛ چنین داده‌هایی باید در سمت سرور و از منبع معتبر بررسی شوند.

 

4. Rate Limiting

کاربر مخرب ممکن است در هر ثانیه صدها پیام ارسال کند. این رفتار می‌تواند باعث فشار روی سرور یا اختلال در مسابقه شود.

باید برای eventهای حساس محدودیت ایجاد کنید:

- حرکت
- شلیک
- ارسال پیام چت
- درخواست خرید
- تغییر تنظیمات
- ثبت پاسخ
- ساخت اتاق

نمونه ساده:


const lastMoveByClient = new Map<string, number>();
private canMove(clientId: string): boolean {
 const now = Date.now();
 const lastMoveAt = lastMoveByClient.get(clientId) ?? 0;
 if (now - lastMoveAt < 50) {
   return false;
 }
 lastMoveByClient.set(clientId, now);
 return true;
}

در پروژه‌های حرفه‌ای‌تر، Redis یا middlewareهای rate limit گزینه مناسب‌تری هستند.

 

 ذخیره نتایج بازی در PostgreSQL

state بازی معمولاً موقت است، اما اطلاعات مهم باید بعد از پایان مسابقه ذخیره شوند.

اطلاعاتی که می‌توانید نگهداری کنید:

- شناسه مسابقه
- نوع بازی
- زمان شروع و پایان
- کاربران شرکت‌کننده
- برنده
- امتیازها
- تاریخچه حرکت‌ها
- دلیل پایان مسابقه
- هزینه ورود یا جایزه
- وضعیت پرداخت

نمونه ساختار پیشنهادی جدول:


CREATE TABLE game_matches (
 id UUID PRIMARY KEY,
 room_id VARCHAR(100) NOT NULL,
 game_type VARCHAR(50) NOT NULL,
 status VARCHAR(30) NOT NULL,
 winner_user_id UUID NULL,
 started_at TIMESTAMP NULL,
 finished_at TIMESTAMP NULL,
 created_at TIMESTAMP NOT NULL DEFAULT NOW()
);

و جدول شرکت‌کنندگان:


CREATE TABLE game_match_players (
 id UUID PRIMARY KEY,
 match_id UUID NOT NULL REFERENCES game_matches(id),
 user_id UUID NOT NULL,
 score INT NOT NULL DEFAULT 0,
 result VARCHAR(30) NULL,
 joined_at TIMESTAMP NOT NULL DEFAULT NOW(),
 left_at TIMESTAMP NULL
);

نکته مهم: نوشتن مداوم تمام state بازی در دیتابیس توصیه نمی‌شود، مگر اینکه بازی شما نیاز جدی به persistence داشته باشد. برای داده‌های لحظه‌ای معمولاً memory یا Redis مناسب‌تر است.

 

 چالش‌ها و محدودیت‌های Colyseus

با وجود مزایای زیاد، Colyseus برای همه پروژه‌ها بهترین انتخاب نیست.

 

 1. برای بازی‌های AAA سه‌بعدی سنگین مناسب نیست

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

نمونه‌ها:

- بازی‌های FPS بسیار رقابتی
- بازی‌های MMORPG بسیار بزرگ
- بازی‌های جهان‌باز عظیم
- بازی‌هایی با فیزیک بسیار پیچیده
- بازی‌های Battle Royale با صدها بازیکن در یک نقشه

Colyseus همچنان می‌تواند در بخشی از چنین معماری‌هایی استفاده شود، اما احتمالاً بهینه‌سازی، shard کردن دنیا، سرویس‌های جداگانه و طراحی شبکه پیشرفته لازم خواهد بود.

 

 2. نیاز به دانش شبکه و معماری بازی

استفاده از Colyseus باعث نمی‌شود چالش‌های شبکه کاملاً حذف شوند. در بازی‌های سریع همچنان باید مفاهیمی مانند زیر را درک کنید:

- latency
- packet loss
- client prediction
- interpolation
- extrapolation
- reconciliation
- tick rate
- authoritative server
- cheating prevention

 

 3. state بسیار بزرگ می‌تواند مشکل‌ساز باشد

اگر state اتاق بسیار حجیم باشد یا تغییرات زیادی در هر tick رخ دهد، ممکن است مصرف شبکه و پردازش بالا برود.

راهکارها:

- نگهداری فقط داده‌های ضروری در state
- ارسال پیام‌های جداگانه برای eventهای موقت
- کاهش نرخ بروزرسانی
- محدود کردن تعداد بازیکنان هر room
- تقسیم نقشه به roomهای کوچک‌تر
- حذف داده‌های غیرضروری
- استفاده از visibility یا interest management در طراحی بازی

 

 پیشنهاد معماری برای یک بازی آنلاین واقعی

برای ساخت یک پروژه حرفه‌ای، می‌توانید معماری زیر را در نظر بگیرید:


Frontend Game Client
  ├── React / Phaser / Unity
  └── colyseus.js
           │
           │ WebSocket
           ▼
Game Server
  ├── Colyseus
  ├── Game Rooms
  ├── Matchmaking
  ├── State Synchronization
  └── Game Rules
           │
           ├── Redis
           │   ├── Presence
           │   ├── Session
           │   ├── Cache
           │   └── Distributed Coordination
           │
           ├── PostgreSQL
           │   ├── Users
           │   ├── Matches
           │   ├── Leaderboard
           │   └── Transactions
           │
           └── Main Backend API
               ├── NestJS یا ASP.NET Core
               ├── Authentication
               ├── Payment
               ├── Wallet
               ├── Admin Panel
               └── Reporting

این معماری به‌خصوص برای پروژه‌هایی که پرداخت، کیف پول، مسابقه پولی یا سیستم رتبه‌بندی دارند، منطقی است.

 

 بهترین سناریوها برای استفاده از Colyseus

Colyseus انتخاب بسیار خوبی است اگر پروژه شما یکی از ویژگی‌های زیر را داشته باشد:

- بازی چندنفره مبتنی بر اتاق
- بازی نوبتی یا کارتی
- مسابقات کوتاه و سریع
- نیاز به matchmaking
- نیاز به sync شدن state میان بازیکنان
- استفاده از Node.js و TypeScript
- نیاز به کنترل کامل منطق بازی در سمت سرور
- نیاز به reconnect بازیکنان
- توسعه بازی با Phaser، Unity یا React
- نیاز به جلوگیری از تقلب در بخش‌های مهم بازی

 

 چه زمانی بهتر است از Colyseus استفاده نکنیم؟

در شرایط زیر شاید ابزار دیگری مناسب‌تر باشد:

سناریو گزینه پیشنهادی
چت سادهSocket.IO یا SignalR 
اعلان لحظه‌ای در پنلSocket.IO، SignalR یا SSE
CRUD معمولیREST API یا GraphQL
داشبورد سازمانی زندهSignalR یا Socket.IO
بازی سه‌بعدی عظیم و بسیار رقابتی معماری اختصاصی یا سرویس‌های تخصصی
اپلیکیشن ساده بدون state مشترکWebSocket ساده یا SSE


 

 جمع‌بندی

Colyseus یک فریمورک قدرتمند برای ساخت بازی‌های آنلاین چندنفره و اپلیکیشن‌های Real-time مبتنی بر Node.js است. مهم‌ترین مزیت آن، فراهم کردن ساختار آماده برای مدیریت Room، matchmaking، همگام‌سازی state، مدیریت lifecycle بازیکنان و ارتباط WebSocket است.

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

برای پروژه‌های حرفه‌ای، بهتر است Colyseus را در کنار ابزارهایی مانند PostgreSQL، Redis، JWT، Docker و یک API اصلی با NestJS یا ASP.NET Core استفاده کنید. در این حالت، Colyseus مسئول منطق لحظه‌ای و مدیریت مسابقه خواهد بود و بک‌اند اصلی مسئول کاربران، پرداخت‌ها، گزارش‌گیری، مدیریت داده‌های دائمی و پنل ادمین می‌شود.