Agent skill
gamebuddies-game-generator
Generate complete multiplayer games for GameBuddies.io platform with full integration including streamer mode, lobby system, chat, WebRTC video, and real-time gameplay. Creates server logic, client UI, type definitions, and game mechanics following GameBuddies template patterns.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/testing/gamebuddies-game-generator
SKILL.md
GameBuddies Game Generator
You are an expert at creating multiplayer games for the GameBuddies.io platform using the proven template architecture. This skill guides you through generating complete, production-ready multiplayer games with all GameBuddies features built-in.
Core Capabilities
Generate games with these integrated features:
- Streamer Mode: Hidden room codes, GameBuddies API integration, return-to-lobby flow
- Lobby System: Pre-game waiting room with player list, settings, and chat
- Real-time Chat: Message history, emoji picker, 500-char limit
- WebRTC Video: Peer-to-peer video chat with mobile support, TURN servers
- Player Management: Host controls, kick functionality, 30-second disconnect grace period
- Game Logic: Turn-based, real-time, or phase-based gameplay patterns
- Type Safety: Full TypeScript with matching client/server types
Game Generation Process
Step 1: Gather Requirements
Ask the user these questions to understand their game:
- Game Concept: What's the game about? (e.g., "A reaction game where players press buttons when colors match")
- Game Type:
- Turn-based (players take sequential turns)
- Real-time (all players act simultaneously)
- Phase-based (game progresses through distinct phases)
- Player Count: Min and max players (2-8 recommended)
- Win Condition: How does a player win? (e.g., "First to 10 points", "Most points after 5 rounds")
- Special Features: Any unique mechanics? (e.g., "Power-ups", "Voting system", "Time limits")
Step 2: Define Type Structures
Create type definitions in both server/types.ts and client/src/types.ts:
Key Types to Define:
// GameData - The core game state
interface GameData {
currentRound: number;
maxRounds: number;
// Add game-specific state (scores, turn info, board state, etc.)
}
// Settings - Configurable game parameters
interface Settings {
maxPlayers: number;
// Add game-specific settings (round count, time limits, difficulty, etc.)
}
// Player - Extended player information
interface Player {
id: string;
name: string;
isHost: boolean;
isConnected: boolean;
// Add game-specific player data (score, lives, status, etc.)
}
Pattern to Follow:
- Server types are the source of truth
- Client types mirror server but may add UI-only properties
- Use TypeScript enums for game states and phases
- Keep types synchronized between client and server
Step 3: Implement Server Logic
Edit server/server.ts starting at line 343 in the socket.on('game:start') handler:
3A. Initialize Game State
socket.on('game:start', () => {
const lobby = /* ... find lobby ... */;
// Initialize your game state
lobby.gameData = {
currentRound: 1,
maxRounds: lobby.settings.rounds || 5,
// Initialize game-specific state
scores: lobby.players.reduce((acc, p) => ({ ...acc, [p.id]: 0 }), {}),
currentTurn: lobby.players[0].id,
// etc.
};
lobby.state = 'PLAYING';
io.to(lobby.code).emit('game:started', lobby);
});
3B. Add Game Event Handlers
Add new socket event listeners for your game actions:
socket.on('game:your-action', (data: YourActionData) => {
const lobby = /* ... find and validate ... */;
// Validate action (player's turn, valid move, etc.)
// Update game state
// Check win conditions
// Broadcast update
io.to(lobby.code).emit('game:update', lobby.gameData);
// If game ended
if (isGameOver) {
lobby.state = 'GAME_ENDED';
io.to(lobby.code).emit('game:ended', {
winners: calculateWinners(lobby),
finalScores: lobby.gameData.scores
});
}
});
3C. Validation Patterns
Always validate:
- Player is in the lobby
- It's the player's turn (for turn-based games)
- Move/action is legal
- Lobby is in PLAYING state
Use utilities from server/utils/validation.ts:
sanitizeInput()- Clean user inputvalidatePlayerName()- 1-20 charactersvalidateChatMessage()- 1-500 characters
Step 4: Create Client UI
Replace client/src/components/GameComponent.tsx with your game interface:
4A. Component Structure
import { useSocket } from '../hooks/useSocket';
import { Lobby } from '../types';
interface GameComponentProps {
lobby: Lobby;
currentPlayerId: string;
}
export const GameComponent: React.FC<GameComponentProps> = ({ lobby, currentPlayerId }) => {
const socket = useSocket();
const gameData = lobby.gameData;
// Game-specific handlers
const handleAction = (actionData: any) => {
socket.emit('game:your-action', actionData);
};
return (
<div className="game-container">
{/* Scoreboard */}
{/* Game board/interface */}
{/* Action buttons */}
{/* Status displays */}
</div>
);
};
4B. UI Patterns
Use these consistent patterns:
- Tailwind CSS for styling (bg-blue-500, rounded-lg, etc.)
- Responsive design with mobile-first approach
- Disable buttons when not player's turn
- Visual feedback for actions (animations, color changes)
- Clear status messages ("Waiting for other players", "Your turn!")
4C. Real-time Updates
Listen for server events in useEffect:
useEffect(() => {
socket.on('game:update', (updatedGameData) => {
// Update local state
});
return () => {
socket.off('game:update');
};
}, [socket]);
Step 5: Implement Game Logic Patterns
Choose the pattern that matches your game type:
TURN-BASED PATTERN (Word Chain, Uno, Chess)
// Server tracks currentTurn
gameData.currentTurn = playerIds[nextPlayerIndex];
// Server validates it's player's turn
if (data.playerId !== lobby.gameData.currentTurn) {
socket.emit('error', { message: 'Not your turn' });
return;
}
// Advance turn after valid action
const currentIndex = players.findIndex(p => p.id === gameData.currentTurn);
gameData.currentTurn = players[(currentIndex + 1) % players.length].id;
REAL-TIME PATTERN (Space Invaders, Racing, Battle Royale)
// Server runs game loop (60Hz recommended)
const gameLoop = setInterval(() => {
updateGamePhysics(lobby.gameData);
checkCollisions(lobby.gameData);
io.to(lobby.code).emit('game:update', lobby.gameData);
}, 16); // ~60fps
// Store interval reference to clear on game end
lobby.gameData.gameLoopInterval = gameLoop;
PHASE-BASED PATTERN (Color Memory, Trivia, Drawing Games)
// Define phases as enum
enum GamePhase {
SHOWING = 'showing',
ANSWERING = 'answering',
SCORING = 'scoring'
}
gameData.currentPhase = GamePhase.SHOWING;
// Transition phases with timers
setTimeout(() => {
gameData.currentPhase = GamePhase.ANSWERING;
io.to(lobby.code).emit('game:update', lobby.gameData);
}, 3000);
Step 6: Add Settings and Configuration
Update client/src/components/Lobby.tsx to add game-specific settings:
// Add settings controls in the Lobby component
<div className="settings-panel">
<label>Rounds</label>
<input
type="number"
value={settings.rounds}
onChange={(e) => updateSettings({ rounds: parseInt(e.target.value) })}
min={1}
max={10}
/>
</div>
Settings are controlled by the host and synced via socket.emit('settings:update').
Step 7: Test and Validate
Ensure your game handles:
- Player disconnects during game (30-second grace period)
- Host leaving (host transfer to next player)
- Invalid moves/actions (proper error messages)
- Edge cases (one player left, all players disconnect)
- GameBuddies integration (streamer mode hides room code)
- Game restart functionality
- Return to GameBuddies button appears (GameBuddies rooms only)
Reference Game Examples
The template includes 3 complete reference games in the Games/ folder:
-
Color Memory (Phase-based)
- Shows: Phase transitions, timing, scoring system
- Pattern: WATCHING → REPLAYING → SCORING
- 600 lines
-
Word Chain (Turn-based)
- Shows: Turn rotation, validation, voting system
- Pattern: Sequential turns with timer
- 800 lines
-
Space Invaders (Real-time)
- Shows: Game loop, physics, collision detection
- Pattern: 60Hz server-side simulation
- 700 lines
Key Files to Modify
| File | Purpose | What to Add |
|---|---|---|
server/types.ts |
Type definitions | GameData, Settings interfaces |
client/src/types.ts |
Client types | Mirror server types + UI types |
server/server.ts |
Game logic | Event handlers starting at line 343 |
client/src/components/GameComponent.tsx |
Game UI | Replace entire component |
client/src/components/Lobby.tsx |
Settings | Add game-specific settings controls |
server/utils/validation.ts |
Validation | Add game-specific validators |
Built-in Features (Already Implemented)
Your game automatically includes:
✅ Lobby System - Pre-game waiting room with player list ✅ Chat System - Real-time messaging with emoji picker ✅ WebRTC Video - Peer-to-peer video chat with mobile support ✅ Player Management - Kick, disconnect handling, host transfer ✅ GameBuddies Integration - Streamer mode, session tokens, return flow ✅ Return Button - Appears only in GameBuddies rooms ✅ Responsive Design - Mobile and desktop support ✅ Type Safety - Full TypeScript coverage ✅ Validation - Input sanitization and error handling ✅ WebRTC Features - Virtual backgrounds, 3D avatars, audio effects
GameBuddies API Integration
Games automatically integrate with GameBuddies.io:
Streamer Mode Flow:
- Streamer clicks "Launch Game" on gamebuddies.io
- Game receives session token via URL:
?session=TOKEN&players=4&role=gm - Client resolves token to room code via GameBuddies API
- Room code is hidden from UI (prevents stream sniping)
- Players join via safe invite links
- Return button appears automatically
- On return, players redirect back to gamebuddies.io lobby
Status Updates: Server automatically sends player status to GameBuddies API:
connected- Player joinedin_game- Game starteddisconnected- Player left
Return Flow:
// Client emits return request
socket.emit('gamebuddies:return', { returnAll: true });
// Server calls GameBuddies API
const { returnUrl } = await requestReturnToGameBuddies(lobby.code, true);
// Server sends redirect URL to clients
socket.emit('gamebuddies:return-redirect', { returnUrl });
Socket Event Reference
Standard Events (Already Handled):
lobby:create,lobby:join- Room managementgame:start,game:end,game:restart- Game lifecyclechat:send-message- Chat systemplayer:kick- Player managementgamebuddies:return- Return to GameBuddieswebrtc:offer,webrtc:answer,webrtc:ice-candidate- Video chat
Your Custom Events:
game:your-action- Game-specific actions (YOU define these)game:update- Broadcast game state changes
Code Generation Guidelines
- Always use TypeScript - No plain JavaScript
- Follow existing patterns - Match the template's code style
- Validate on server - Never trust client input
- Emit updates immediately - Use
io.to(lobby.code).emit()after state changes - Handle edge cases - Disconnects, invalid moves, race conditions
- Add comments - Explain game-specific logic
- Use Tailwind CSS - For consistent styling
- Test streamer mode - Verify room code is hidden
- Implement restart - Clear game state properly in
game:restarthandler - Mobile-friendly - Responsive design, touch-friendly buttons
Example: Generating a Simple Dice Game
User Request: "Create a dice rolling game where players take turns rolling dice, highest total after 5 rounds wins"
Your Process:
- Define Types:
// server/types.ts & client/src/types.ts
interface GameData {
currentRound: number;
maxRounds: number;
currentTurn: string; // player ID
lastRoll: number | null;
scores: { [playerId: string]: number };
rolls: { [playerId: string]: number[] };
}
interface Settings {
maxPlayers: number;
rounds: number;
}
- Initialize Game (server/server.ts line 343):
lobby.gameData = {
currentRound: 1,
maxRounds: lobby.settings.rounds || 5,
currentTurn: lobby.players[0].id,
lastRoll: null,
scores: lobby.players.reduce((acc, p) => ({ ...acc, [p.id]: 0 }), {}),
rolls: lobby.players.reduce((acc, p) => ({ ...acc, [p.id]: [] }), {})
};
- Add Event Handler:
socket.on('game:roll-dice', () => {
const lobby = findLobbyBySocketId(socket.id);
if (!lobby || lobby.state !== 'PLAYING') return;
const player = lobby.players.find(p => p.id === socket.id);
if (!player || lobby.gameData.currentTurn !== player.id) {
socket.emit('error', { message: 'Not your turn' });
return;
}
// Roll dice
const roll = Math.floor(Math.random() * 6) + 1;
lobby.gameData.lastRoll = roll;
lobby.gameData.scores[player.id] += roll;
lobby.gameData.rolls[player.id].push(roll);
// Next turn
const currentIndex = lobby.players.findIndex(p => p.id === player.id);
const nextPlayer = lobby.players[(currentIndex + 1) % lobby.players.length];
lobby.gameData.currentTurn = nextPlayer.id;
// Check round completion
if (nextPlayer.id === lobby.players[0].id) {
lobby.gameData.currentRound++;
}
// Check game end
if (lobby.gameData.currentRound > lobby.gameData.maxRounds) {
const winner = Object.entries(lobby.gameData.scores)
.sort(([,a], [,b]) => b - a)[0];
lobby.state = 'GAME_ENDED';
io.to(lobby.code).emit('game:ended', {
winnerId: winner[0],
finalScores: lobby.gameData.scores
});
} else {
io.to(lobby.code).emit('game:update', lobby.gameData);
}
});
- Create UI (client/src/components/GameComponent.tsx):
export const GameComponent: React.FC<GameComponentProps> = ({ lobby, currentPlayerId }) => {
const socket = useSocket();
const gameData = lobby.gameData;
const isMyTurn = gameData.currentTurn === currentPlayerId;
const rollDice = () => {
socket.emit('game:roll-dice');
};
return (
<div className="p-8">
<div className="text-center mb-6">
<h2 className="text-2xl font-bold">Round {gameData.currentRound}/{gameData.maxRounds}</h2>
</div>
<div className="grid grid-cols-2 gap-4 mb-6">
{lobby.players.map(player => (
<div key={player.id} className={`p-4 rounded ${player.id === gameData.currentTurn ? 'bg-blue-500' : 'bg-gray-700'}`}>
<div className="font-bold">{player.name}</div>
<div>Score: {gameData.scores[player.id]}</div>
</div>
))}
</div>
{gameData.lastRoll && (
<div className="text-center text-4xl mb-4">
🎲 {gameData.lastRoll}
</div>
)}
<button
onClick={rollDice}
disabled={!isMyTurn}
className={`w-full py-4 rounded text-xl ${isMyTurn ? 'bg-green-500 hover:bg-green-600' : 'bg-gray-500 cursor-not-allowed'}`}
>
{isMyTurn ? 'Roll Dice!' : 'Waiting for other player...'}
</button>
</div>
);
};
- Add Settings (client/src/components/Lobby.tsx):
<div className="mb-4">
<label className="block mb-2">Number of Rounds</label>
<input
type="number"
value={settings.rounds || 5}
onChange={(e) => updateSettings({ rounds: parseInt(e.target.value) })}
min={1}
max={20}
className="w-full p-2 rounded bg-gray-700"
/>
</div>
Best Practices
- Server Authority - All game logic executes on server, clients only display state
- Immediate Feedback - Emit updates right after state changes
- Graceful Degradation - Handle disconnects, errors, edge cases
- Clear Status - Show whose turn, what's happening, how to play
- Mobile Support - Large touch targets, responsive layout
- Performance - Avoid sending entire lobby object, send deltas when possible
- Security - Validate all inputs, sanitize chat messages
- Consistency - Follow existing code patterns and naming conventions
Deployment
The template is pre-configured for Render.com:
- Automatic reverse proxy support
- Environment variables:
PORT,NODE_ENV,CORS_ORIGIN - Health check endpoint:
/health - Build commands:
npm run build:render - Start command:
node server/dist/server.js
Common Pitfalls to Avoid
❌ Client-side game logic (creates cheating opportunities)
❌ Forgetting to validate player's turn
❌ Not handling disconnects
❌ Hardcoding room codes (breaks streamer mode)
❌ Forgetting to clear intervals/timeouts on game end
❌ Not synchronizing types between client/server
❌ Missing return button (required for GameBuddies)
❌ Emitting to socket instead of room (socket.emit vs io.to(code).emit)
Output Format
When generating a game, provide:
- Type definitions (both client and server)
- Server event handlers (initialization + custom events)
- GameComponent.tsx (complete replacement)
- Settings UI (additions to Lobby.tsx)
- Testing checklist (edge cases to verify)
- Game rules (brief explanation for players)
Summary
This skill transforms game ideas into production-ready multiplayer games with:
- Complete GameBuddies.io integration (streamer mode, return flow, API calls)
- Real-time multiplayer using Socket.IO
- WebRTC video chat with mobile support
- Chat system with emoji picker
- Lobby system with player management
- Type-safe TypeScript throughout
- Responsive, mobile-friendly UI
- Server-authoritative game logic
- Proper validation and error handling
Follow the patterns, reference the examples, and create engaging multiplayer experiences for the GameBuddies platform!
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?