# MemeMind Client-Server Integration Guide ## Overview This document explains how the Cocos Creator client communicates with the MemeMind-Server backend and what extensions would be needed to support the full game flow including user authentication, progress tracking, and point/life management. --- ## Part 1: Current Integration (Read-Only) ### Current API Call: Get All Levels **Client File**: `/Users/richard/Documents/code/cocosProject/mp-xieyingeng/assets/scripts/managers/LevelDataManager.ts` **Current Implementation**: ```typescript async initialize(): Promise { try { // Initialize() is called by PageLoading during startup const response = await HttpUtil.get( 'https://ilookai.cn/api/v1/wechat-game/levels' ); if (response.success && response.data?.levels) { this._apiData = response.data.levels; this._levelDataCache.clear(); // Preload next level images asynchronously this.preloadNextLevel(0); } } catch (error) { console.error('Failed to load level data', error); } } ``` **Server Endpoint**: ``` GET /api/v1/wechat-game/levels Status: 200 Response: ApiResponseDto ``` **Response Format**: ```typescript { success: true, data: { levels: [ { level: 1, // Level number (1-indexed) id: "level-001", // Unique ID imageUrl: "https://...", // Level image URL answer: "meme", // Correct answer hint1: "image", // First hint (free) hint2: "funny", // Second hint (costs 1 life) hint3: null, // Third hint (costs 1 life) sortOrder: 0, // Display order createdAt: "2026-04-01T...", updatedAt: "2026-04-05T..." }, ... ], total: 50 }, message: null, timestamp: "2026-04-05T10:30:00Z" } ``` ### Data Flow ``` Client Startup │ ▼ PageLoading.ts: _startPreload() │ ├─ LevelDataManager.initialize() │ │ │ └─> HttpUtil.get('/api/v1/wechat-game/levels') │ │ │ ▼ │ MemeMind-Server │ │ │ ├─> WechatGameController.getAllLevels() │ ├─> WechatGameService.getAllLevels() │ ├─> LevelRepository.findAllOrdered() │ └─> MySQL: SELECT * FROM levels ORDER BY sort_order │ │ Response returned │ │ │ ▼ │ LevelDataManager._apiData = levels │ Preload images │ ├─ Progress: 80% -> 100% │ ▼ PageHome displayed │ ▼ User clicks "Start Game" │ ▼ PageLevel loaded │ ├─> Reads from _apiData ├─> Displays level image, hints, input └─> Ready for gameplay ``` --- ## Part 2: Missing Features for Full Integration ### 1. User Authentication **What's needed**: - WeChat OpenID extraction via `wx.login()` - Backend user registration/login endpoint - JWT token generation and validation - User context in requests **Implementation Plan**: **Server Addition**: ```typescript // src/modules/users/users.module.ts @Module({ controllers: [UsersController], providers: [UsersService, UsersRepository], imports: [TypeOrmModule.forFeature([User])], }) export class UsersModule {} // src/modules/users/entities/user.entity.ts @Entity('users') export class User { @PrimaryGeneratedColumn('uuid') id: string; @Column({ unique: true }) wxOpenId: string; // WeChat OpenID @Column() nickname: string; @Column({ default: 10 }) currentLives: number; @Column({ default: 0 }) currentLevelIndex: number; @Column({ default: 0 }) totalPoints: number; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; } // Endpoint: POST /api/v1/users/login @Post('login') async login(@Body() dto: LoginDto): Promise> { // dto.code from wx.login() // Exchange code for wxOpenId via WeChat API // Create or fetch user // Generate JWT token // Return user + token } ``` **Client Addition**: ```typescript // In PageLoading.ts or main.ts async function initializeUser() { try { const code = await wx.login(); const response = await HttpUtil.post('/api/v1/users/login', { code: code.code }); if (response.success) { const { user, token } = response.data; StorageManager.setToken(token); // New: Store JWT StorageManager.setUserId(user.id); // New: Store user ID return user; } } catch (error) { console.error('Login failed', error); } } ``` --- ### 2. Level Submission & Answer Validation **What's needed**: - POST endpoint for level submissions - Answer validation (case-insensitive, trim whitespace) - Update user progress - Award points/lives - Handle wrong answers **Implementation Plan**: **Server Addition**: ```typescript // src/modules/levels/level-submission.module.ts @Module({ controllers: [LevelSubmissionController], providers: [LevelSubmissionService], imports: [ TypeOrmModule.forFeature([UserProgress, Level]), UsersModule, WechatGameModule, ], }) export class LevelSubmissionModule {} // Endpoint: POST /api/v1/levels/:levelId/submit @Post(':levelId/submit') @UseGuards(JwtAuthGuard) // Require authentication async submitAnswer( @Param('levelId') levelId: string, @Body() dto: SubmitAnswerDto, @Req() request: any, ): Promise> { // request.user.id from JWT // Validate answer (case-insensitive, trim) // If correct: // - Award 1 life // - Update currentLevelIndex // - Record submission // If wrong: // - Record wrong attempt // - Maybe deduct lives? // Return result } // src/modules/levels/dto/submit-answer.dto.ts export class SubmitAnswerDto { @IsString() @IsNotEmpty() answer: string; @IsNumber() timeTaken: number; // Seconds to solve @IsNumber() hintsUsed: number; // How many hints revealed } export class SubmitAnswerResponseDto { success: boolean; // Correct answer? message: string; // "Correct!" or "Wrong!" newLives: number; // Updated lives newLevel: number; // Next level index pointsEarned: number; // Points for this solve totalPoints: number; // Total accumulated points } ``` **Client Change**: ```typescript // In PageLevel.ts: showSuccess() method private async showSuccess(): void { this.stopCountdown(); this.playSuccessSound(); // NEW: Submit to server try { const token = StorageManager.getToken(); const response = await HttpUtil.post( `/api/v1/levels/${this._currentLevel.id}/submit`, { answer: this._userAnswer, timeTaken: this._elapsedTime, hintsUsed: this._hintsUsed }, { headers: { Authorization: `Bearer ${token}` } } ); if (response.success) { // Update local storage with new progress StorageManager.setLives(response.data.newLives); StorageManager.onLevelCompleted(response.data.newLevel - 1); // Show points earned this.showPointsNotification(response.data.pointsEarned); } } catch (error) { console.error('Submission failed', error); } this._showPassModal(); } ``` --- ### 3. User Progress Tracking **What's needed**: - Endpoint to get user progress - Endpoint to update progress - Sync between client localStorage and server - Handle offline mode **Implementation Plan**: **Server Addition**: ```typescript // Endpoint: GET /api/v1/users/me/progress @Get('me/progress') @UseGuards(JwtAuthGuard) async getProgress( @Req() request: any, ): Promise> { // request.user.id from JWT // Return user progress } export class UserProgressDto { id: string; userId: string; currentLevelIndex: number; maxLevelUnlocked: number; totalPoints: number; currentLives: number; completedLevels: { levelId: string; completedAt: Date; timeTaken: number; hintsUsed: number; }[]; } // Endpoint: POST /api/v1/users/me/progress/sync @Post('me/progress/sync') @UseGuards(JwtAuthGuard) async syncProgress( @Req() request: any, @Body() dto: SyncProgressDto, ): Promise> { // Merge client progress with server // Handle conflicts (prefer latest) // Return merged progress } ``` **Client Update**: ```typescript // In StorageManager.ts: Add sync methods static async syncWithServer(): Promise { try { const token = this.getToken(); const localProgress = this.getCurrentLevelIndex(); const localLives = this.getLives(); const response = await HttpUtil.post( '/api/v1/users/me/progress/sync', { currentLevelIndex: localProgress, currentLives: localLives, }, { headers: { Authorization: `Bearer ${token}` } } ); if (response.success) { // Update local with server's merged data this.setLives(response.data.currentLives); // Update progress for each completed level response.data.completedLevels.forEach((level) => { this.onLevelCompleted(level.levelIndex); }); } } catch (error) { console.error('Sync failed', error); // Continue with local data } } // Call on app startup and periodically ``` --- ### 4. Hint Usage & Cost Management **What's needed**: - Track which hints have been used - Deduct lives when using premium hints - Prevent excessive hint usage **Implementation Plan**: **Server Addition**: ```typescript // Track hint usage per level per user @Entity('level_hints') export class LevelHint { @PrimaryGeneratedColumn('uuid') id: string; @ManyToOne(() => User) user: User; @Column() levelId: string; @Column() hint1Revealed: boolean; // Always free @Column() hint2Revealed: boolean; // Costs 1 life @Column() hint3Revealed: boolean; // Costs 1 life @CreateDateColumn() createdAt: Date; } // Endpoint: POST /api/v1/levels/:levelId/reveal-hint @Post(':levelId/reveal-hint') @UseGuards(JwtAuthGuard) async revealHint( @Param('levelId') levelId: string, @Body() dto: RevealHintDto, // { hintNumber: 2 } @Req() request: any, ): Promise> { // Validate hint number // Check if already revealed // If hint 2 or 3: deduct 1 life // Update hint tracking // Return hint text } ``` **Client Change**: ```typescript // In PageLevel.ts: onUnlockClue() method private async onUnlockClue(clueIndex: number): void { const token = StorageManager.getToken(); // Hint index 1 is always free (hint1) // Hints 2 and 3 cost 1 life each if (clueIndex > 0 && !this._freeCluePassed) { const currentLives = StorageManager.getLives(); if (currentLives <= 0) { this._showToast('没有生命了!(No lives left!)'); return; } } try { const response = await HttpUtil.post( `/api/v1/levels/${this._currentLevel.id}/reveal-hint`, { hintNumber: clueIndex + 1 }, { headers: { Authorization: `Bearer ${token}` } } ); if (response.success) { const hint = response.data.hintText; this._showHint(hint); // Deduct life if premium hint if (clueIndex > 0) { StorageManager.consumeLife(); StorageManager.setLives(response.data.remainingLives); this._updateLivesDisplay(); } } } catch (error) { console.error('Failed to unlock hint', error); } } ``` --- ### 5. Leaderboard & Statistics **What's needed**: - Track total points per user - Track completion time - Leaderboard endpoint - User statistics endpoint **Implementation Plan**: **Server Addition**: ```typescript // Endpoint: GET /api/v1/leaderboard?limit=100 @Get('leaderboard') async getLeaderboard( @Query('limit') limit: number = 100, ): Promise> { // Select top users by totalPoints // Include rank, nickname, points, completedLevels } // Endpoint: GET /api/v1/users/me/statistics @Get('me/statistics') @UseGuards(JwtAuthGuard) async getStatistics( @Req() request: any, ): Promise> { // Return user statistics // Total levels completed // Average time per level // Total hints used // Current streak, etc. } export class StatisticsDto { totalLevelsCompleted: number; currentLevelIndex: number; totalPoints: number; currentLives: number; averageTimePerLevel: number; // Seconds totalTimeSpent: number; // Seconds totalHintsUsed: number; perfectSolves: number; // Solved in first try longestStreak: number; } ``` --- ## Part 3: API Authentication Pattern ### JWT Guard Implementation **Server**: ```typescript // src/modules/auth/guards/jwt.guard.ts @Injectable() export class JwtAuthGuard implements CanActivate { constructor(private readonly configService: ConfigService) {} canActivate(context: ExecutionContext): boolean { const request = context.switchToHttp().getRequest(); const token = this.extractTokenFromHeader(request); if (!token) { throw new UnauthorizedException('No token provided'); } try { const secret = this.configService.get('JWT_SECRET'); const decoded = verify(token, secret); request.user = decoded; // Attach to request return true; } catch (error) { throw new UnauthorizedException('Invalid token'); } } private extractTokenFromHeader(request): string | undefined { const [type, token] = request.headers.authorization?.split(' ') ?? []; return type === 'Bearer' ? token : undefined; } } // Usage on endpoints @UseGuards(JwtAuthGuard) @Get('me/progress') async getProgress(@Req() request: any) { const userId = request.user.id; // From JWT payload // ... } ``` **Client**: ```typescript // In HttpUtil.ts: Add auth header static async post( url: string, data: any, options?: RequestOptions ): Promise> { const token = StorageManager.getToken(); const headers = { ...options?.headers, 'Content-Type': 'application/json', }; if (token) { headers['Authorization'] = `Bearer ${token}`; } // Make request with headers } ``` --- ## Part 4: Data Sync Strategy ### Scenario 1: Online Mode ``` User completes level │ ▼ [Client] Submits answer │ ├─ HTTP POST /api/v1/levels/:id/submit │ ▼ [Server] Validates and updates │ ├─ Check answer ├─ Award life/points ├─ Update progress ├─ Store submission │ ▼ [Response] Returned to client │ ├─ Update localStorage ├─ Show success modal ├─ Move to next level │ ▼ Progress synced ``` ### Scenario 2: Offline Mode ``` User completes level (no connection) │ ├─ Submit fails (no network) │ ▼ [Client] Stores locally │ ├─ StorageManager.recordOfflineSubmission() ├─ Update lives/progress locally ├─ Show success modal (assume correct) │ ▼ When connection returns │ ├─ StorageManager.syncPendingSubmissions() │ ▼ [Server] Receives batch of submissions │ ├─ Validate all answers ├─ Apply corrections if needed ├─ Return merged state │ ▼ [Client] Reconciles state │ ├─ If conflicts: server wins ├─ Update localStorage ├─ Show notification of changes │ ▼ Progress synced ``` --- ## Part 5: Implementation Roadmap ### Phase 1: Basic Auth (Week 1) - [ ] User entity & table - [ ] Login endpoint with wx.login() code exchange - [ ] JWT token generation - [ ] JwtAuthGuard for protected routes - [ ] Client login integration - [ ] Token storage in StorageManager ### Phase 2: Progress Tracking (Week 2) - [ ] UserProgress entity & table - [ ] GET /api/v1/users/me/progress - [ ] POST /api/v1/levels/:id/submit - [ ] Update client PageLevel.ts to submit answers - [ ] Sync endpoint for merging progress - [ ] Offline submission queue ### Phase 3: Hint System Integration (Week 3) - [ ] LevelHint entity & tracking - [ ] POST /api/v1/levels/:id/reveal-hint - [ ] Life deduction logic - [ ] Client hint unlock cost ### Phase 4: Leaderboard & Stats (Week 4) - [ ] Statistics calculation - [ ] Leaderboard endpoint - [ ] Client leaderboard page - [ ] Personal statistics page ### Phase 5: Polish & Optimization (Week 5) - [ ] Caching layer (@nestjs/cache-manager) - [ ] Rate limiting (@nestjs/throttler) - [ ] Request logging middleware - [ ] Performance monitoring - [ ] Database indexing optimization --- ## Part 6: Environment Configuration ### Server .env.production ```bash NODE_ENV=production PORT=3000 # Database DB_HOST=production-db-host DB_PORT=3306 DB_USERNAME=prod_user DB_PASSWORD=secure_password DB_DATABASE=meme_mind_prod # Authentication JWT_SECRET=very-secure-secret-key-32-chars-min JWT_EXPIRATION=7d # WeChat WECHAT_APPID=your_wechat_appid WECHAT_SECRET=your_wechat_secret # API API_BASE_URL=https://ilookai.cn/api CORS_ORIGIN=https://yourdomain.com ``` ### Client Storage Keys ```typescript // New keys needed: - 'auth_token' → JWT token - 'user_id' → Current user ID - 'offline_submissions' → Queue of submissions to send - 'last_sync' → Timestamp of last sync ``` --- ## Part 7: Error Handling ### Common API Errors ```typescript // 401 Unauthorized { success: false, data: null, message: 'Invalid token', path: '/api/v1/users/me/progress' } // 404 Not Found { success: false, data: null, message: 'Level with id "xyz" not found', path: '/api/v1/levels/xyz' } // 400 Bad Request (validation) { success: false, data: null, message: 'Validation failed: answer must be a string', path: '/api/v1/levels/123/submit' } // 500 Server Error { success: false, data: null, message: 'Internal server error', path: '/api/v1/levels' } ``` ### Client Handling ```typescript async function handleApiError(error: unknown) { if (axios.isAxiosError(error)) { const status = error.response?.status; const message = error.response?.data?.message; if (status === 401) { // Token expired - redirect to login StorageManager.clearToken(); navigateTo('PageHome'); } else if (status === 404) { showToast(`Not found: ${message}`); } else if (status === 400) { showToast(`Invalid input: ${message}`); } else { showToast(`Error: ${message || 'Unknown error'}`); } } } ``` --- ## Summary The current integration only handles reading level data. To support the full game loop with: - ✅ User authentication (JWT) - ✅ Answer submission & validation - ✅ Progress tracking & sync - ✅ Hint system with cost management - ✅ Leaderboard & statistics You need to implement the endpoints, models, and client logic described in this guide. The roadmap suggests a 5-week implementation with phases for each feature. --- *Generated: 2026-04-05 | For MemeMind Project*