Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

สร้าง Real-Time Multiplayer Tic-Tac-Toe Game ที่ Scalable ด้วย Node.js, Socket.IO และ Redis

สร้าง Real-Time Multiplayer Tic-Tac-Toe Game ที่ Scalable ด้วย Node.js, Socket.IO และ Redis

ใน tutorial นี้ เราจะสร้าง real-time multiplayer Tic-Tac-Toe game ใช้ Node.js, Socket.IO, และ Redis เกมนี้อนุญาตให้สองผู้เล่นเชื่อมต่อจาก browser tabs ต่างกัน เล่นสลับกัน และเห็น real-time updates ขณะเล่น เราจะใช้ Redis จัดการ game state synchronization ข้าม multiple WebSocket servers ทำให้แอปพลิเคชัน scalable

จบ tutorial คุณจะได้เกมที่ทำงานสมบูรณ์ พร้อม real-time capabilities และความเข้าใจมั่นใจเกี่ยวกับการใช้ WebSockets และ Redis สร้าง real-time applications ที่ scalable

สิ่งที่คุณจะได้เรียนรู้

  • วิธีใช้ Socket.IO สำหรับ real-time communication
  • วิธีใช้ Redis Pub/Sub synchronize game state ข้าม multiple clients
  • วิธีตั้งค่า scalable WebSocket server architecture

ข้อกำหนดเบื้องต้น (Prerequisites)

ก่อนเริ่ม ตรวจสอบว่าติดตั้งแล้ว:

  • Node.js (v16 หรือสูงกว่า)
  • Redis
  • Docker (optional, สำหรับรัน Redis ใน container)
  • ความรู้พื้นฐาน JavaScript, Node.js, และ WebSockets

สารบัญ

  • Project Overview
  • Step 1: Setting Up Your Development Environment
  • Step 2: Setting Up the Project
  • Step 3: Implementing the WebSocket Server with Redis
  • Step 4: Implement the React Frontend interface
  • Step 5: Running the Application
  • Step 6: Viewing Redis Messages in Real-Time
  • Demo
  • Conclusion

Project Overview

เราจะสร้าง real-time Tic-Tac-Toe game พร้อม features:

  • Two players เชื่อมต่อและเล่นเกมได้
  • Game board update แบบ real-time ข้าม browser tabs ต่างกัน
  • Game ประกาศ winner หรือ draw เมื่อ board เต็ม

เราจะใช้:

  • Node.js กับ Socket.IO จัดการ WebSocket connections
  • Redis Pub/Sub จัดการ game state synchronization ข้าม clients

Step 1: Setting Up Your Development Environment

Installing Node.js

ตรวจสอบว่า Node.js ติดตั้งอยู่:

node -v

หายังไม่มี ดาวน์โหลดจาก Node.js

Installing Redis

ติดตั้ง Redis locally หรือรันใน Docker container

macOS (Using Homebrew)

ตรวจสอบว่า Homebrew ติดตั้งอยู่ก่อนรันคำสั่งด้านล่าง:

brew install redis
brew services start redis

Verify Redis container รันอยู่:

redis-cli ping

ควรเห็น:

PONG

Using Docker to Run Redis

docker run --name redis-server -p 6379:6379 -d redis

ตรวจสอบ Redis รันอยู่:

docker exec -it redis-server redis-cli ping

Step 2: Setting Up the Project

1. Create the Project Directory

mkdir tic-tac-toe
cd tic-tac-toe
npm init -y

2. Install Dependencies

npm install express socket.io redis dotenv

3. Create Environment Variables

สร้าง .env ใน project root:

PORT=3000
REDIS_HOST=localhost
REDIS_PORT=6379

Step 3: Implementing the WebSocket Server with Redis

ในขั้นตอนนี้ เราจะตั้งค่า WebSocket server จัดการ real-time game interactions 用 Node.js, Socket.IO, และ Redis Server นี้จะจัดการ game state, handle player moves, และ ensure synchronization ข้าม multiple clients ใช้ Redis Pub/Sub

เราจะแยกแต่ละส่วนของโค้ด เพื่อให้คุณเข้าใจว่าทุกอย่างเข้ากันอย่างไร Server Code Explanation

สร้างไฟล์ server.js ใส่โค้ด:

import dotenv from 'dotenv';
import express from 'express';
import http from 'http';
import { Server } from 'socket.io';
import { createClient } from 'redis';
dotenv.config(); // Load environment variables from .env file
const app = express();
const server = http.createServer(app);
const io = new Server(server, {
    cors: {
        origin: "https://localhost:5173",
        methods: ["GET", "POST"],
    }
});
  • dotenv: โหลด environment variables จาก .env file เก็บข้อมูล sensitive เช่น ports และ keys ให้ปลอดภัย
  • express: ตั้งค่า Express server พื้นฐานจัดการ HTTP requests
  • http: สร้าง HTTP server 用 Node's built-in http module ใช้กับ Socket.IO สำหรับ WebSocket communication
  • Socket.IO: library นี้ enable real-time, bidirectional communication ระหว่าง server และ clients
  • CORS Configuration: อนุญาต cross-origin requests จาก frontend รันบน localhost:5173

ต่อไป สร้าง Redis publisher และ subscriber clients ใส่ใน server.js:

// Initialize Redis clients
const pubClient = createClient();
const subClient = createClient();
await pubClient.connect();
await subClient.connect();

เราใช้ Redis จัดการ real-time data synchronization ระหว่าง connected clients

  • pubClient: ใช้ publish messages (เช่น game state updates)
  • subClient: subscribe messages (ฟัง updates)
  • connect(): สร้างการเชื่อมต่อไป Redis server

ใน paradigm นี้ ใช้ client หนึ่ง publish updates อีกอัน subscribe updates นี่ช่วยหลีกเลี่ยง blocking behavior เนื่องจาก Redis clients ใน subscribe mode รับ messages ได้อย่างเดียว

เพื่อ subscribe Redis channels สำหรับ game updates ใส่โค้ดใน server.js:

// Subscribe to the Redis channel for game updates
await subClient.subscribe('game-moves', (message) => {
    gameState = JSON.parse(message);
    io.emit('gameState', gameState);
});
  • subClient.subscribe: ฟัง messages บน channel game-moves
  • ทุกครั้งที่ player ทำ move game state จะ update ใน Redis และ clients ทั้งหมดจะได้รับแจ้ง state ใหม่
  • พารามิเตอร์ message มี game state เป็น string เรา parse เป็น JavaScript object และ broadcast updated state 用 Socket.IO

ต่อไป กำหนด game state และ functions ใส่ใน server.js:

// Define initial game state
let gameState = {
    board: Array(9).fill(null),
    xIsNext: true,
};
// Function to reset the game
function resetGame() {
    gameState = {
        board: Array(9).fill(null),
        xIsNext: true,
    };
}
  • gameState: ติดตาม current state ของ board และ turn ของใคร (xIsNext)
    • Board แทนเป็น array 9 cells (แต่ละอันเป็น 'X', 'O', หรือ null)
    • Flag xIsNext กำหนดว่า turn ของ player ไหน
  • resetGame(): รีเซ็ต board และ turn indicator กลับ initial state เริ่มเกมใหม่ได้

ต่อไป จัดการ WebSocket connections ใส่ใน server.js:

io.on('connection', (socket) => {
    console.log('New client connected:', socket.id);
    // Send current game state to newly connected client
    socket.emit('gameState', gameState);
  • Event io.on('connection') trigger เมื่อ client ใหม่เชื่อมต่อ
  • socket.id: unique identifier สำหรับแต่ละ connected client
  • เราส่ง gameState ปัจจุบันไป client ใหม่ทันที เพื่อให้เห็น board ปัจจุบัน

เพื่อจัดการ player moves ใส่ใน server.js:

// Handle player moves
socket.on('makeMove', (index) => {
    // Prevent making move if cell already taken or game over
    if (gameState.board[index] || calculateWinner(gameState.board)) return;
    // Update board and switch turns
    gameState.board[index] = gameState.xIsNext ? 'X' : 'O';
    gameState.xIsNext = !gameState.xIsNext;
    // Publish updated game state to Redis
    pubClient.publish('game-moves', JSON.stringify(gameState));
    io.emit('gameState', gameState);
});
  • makeMove: event trigger เมื่อ player คลิก cell
    • Validation: เช็คว่า cell ว่างและเกมยังไม่จบก่อนทำ move
    • Updating Game State: หาก move valid update board และสลับ turn
  • Updated game state แล้ว:
    1. Published to Redis: ให้ทุก instances ของ server stay in sync
    2. Broadcasted to all clients: update game board ทุก player ทันที

เพื่อจัดการ game restarts ใส่ใน server.js:

// Handle game restarts
socket.on('restartGame', () => {
    resetGame();
    io.emit('gameState', gameState);
});

จัดการ client disconnection ใส่ใน server.js:

socket.on('disconnect', () => {
    console.log('Client disconnected:', socket.id);
});
});

สุดท้าย เพิ่ม game logic functions ใน server.js:

// Function to check if there's a winner
function calculateWinner(board) {
    const lines = [
        [0, 1, 2], [3, 4, 5], [6, 7, 8],
        [0, 3, 6], [1, 4, 7], [2, 5, 8],
        [0, 4, 8], [2, 4, 6]
    ];
    for (let [a, b, c] of lines) {
        if (board[a] && board[a] === board[b] && board[a] === board[c]) {
            return board[a];
        }
    }
    return null;
}
function isBoardFull(board) {
    return board.every((cell) => cell !== null);
}
  • calculateWinner(): เช็คว่ามี winning combination บน board
  • isBoardFull(): เช็คว่า cells เต็มทั้งหมด แสดงว่า draw

Step 4: Implement the React Frontend interface

ในขั้นตอนนี้ เราสร้าง React frontend เรียบง่ายและ interactive สำหรับ Tic-Tac-Toe game Frontend นี้ให้ players เชื่อมต่อ WebSocket server, ทำ moves, และเห็น game board update แบบ real-time

ใน App.jsx ใส่โค้ด:

import React, { useEffect, useState } from 'react';
import io from 'socket.io-client';
const socket = io('https://localhost:3000');
function App() {
    const [gameState, setGameState] = useState({
        board: Array(9).fill(null),
        xIsNext: true,
        winner: null
    });
    useEffect(() => {
        socket.on('gameState', (state) => {
            setGameState(state);
        });
        return () => socket.off('gameState');
    }, []);
    const handleClick = (index) => {
        if (gameState.board[index] || gameState.winner) return;
        socket.emit('makeMove', index);
    };
    const renderCell = (index) => (
        
    );
    return (
        

Multiplayer Tic-Tac-Toe

{[...Array(9)].map((_, i) => renderCell(i))}
); } export default App;

สรุปวิธี React app แบ่งส่วน:

  • WebSocket Connection:

    • Frontend เชื่อมต่อ server 用 socket.io-client
  • State Management:

    • Game state (gameState) จัดการด้วย React's useState รวม:
      • board (9 cells)
      • Flag xIsNext ระบุ turn ของ player ปัจจุบัน
      • winner status
  • Real-Time Updates:

    • useEffect hook:
      • ฟัง gameState updates จาก server
      • Update local game state เมื่อตรวจพบการเปลี่ยนแปลง
      • Clean up WebSocket listener เมื่อ component unmount
  • Handling Player Moves:

    • handleClick function:
      • เช็คว่า cell ว่างและเกมยังไม่มี winner ก่อน allow move
      • ส่ง event makeMove ไป server พร้อม index ของ cell ที่คลิก
  • Game Board Rendering:

    • renderCell function สร้าง button สำหรับแต่ละ cell บน board
    • Board แสดงเป็น 3x3 grid
  • Restart Game:

    • ปุ่ม "Restart Game" emit event restartGame reset game board สำหรับทุก players
  • User Interface:

    • Layout เรียบง่ายและ interactive ให้ players สลับเล่นและเห็น updates real-time

Step 5: Running the Application

Starting the Backend

เริ่ม backend server เปิด terminal ใหม่ รัน:

cd tic-tac-toe
npm start

Starting the Frontend

เริ่ม React frontend server เปิด terminal ใหม่ รัน (อย่าใช้ terminal เดียวกับ backend เพราะต้องรันพร้อมกัน):

cd tic-tac-toe-client
npm run dev

Accessing the Game

เปิดเบราว์เซอร์ ไป:

https://localhost:5173

Step 6: Viewing Redis Messages in Real-Time

ขณะเกมรันอยู่ คุณดู Redis messages ได้ เพื่อเห็น real-time game state updates

เปิด terminal รัน:

redis-cli SUBSCRIBE game-moves

จะแสดง game updates:

1) "message"
2) "game-moves"
3) "{"board":["X",null,"O",null,"X",null,null,null,null],"xIsNext":false}"

ทุกครั้งที่มี move หรือ game state เปลี่ยน Server จะ publish updated game state ไป channel game-moves ใช้ redis-cli monitor updates แบบ real-time ขณะเล่นเกม

Demo

ใน demo นี้ คุณจะเห็น Tic Tac Toe game รัน locally สาธิต real-time updates ขณะ players สลับเล่น

Gameplay แสดง features เช่น turn switching, board updates, และ game state announcements (winner หรือ draw) นี้ highlight ว่าเกมใช้ WebSocket communication ให้ smooth, interactive experience

สรุป

ยินดีด้วย คุณสร้าง real-time multiplayer Tic-Tac-Toe game ด้วย Node.js, Socket.IO, และ Redis เรียบร้อยแล้ว นี่คือสิ่งที่คุณเรียนรู้:

  • Real-time WebSocket communication 用 Socket.IO
  • Game state management 用 Redis Pub/Sub
  • สร้าง responsive front-end ด้วย React

Next Steps

  • เพิ่ม player authentication
  • Implement chat feature
  • Deploy application ไป cloud provider เพื่อ scalability

Happy coding!

Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. Get started