一键导入
dev-multiplayer-colyseus-client
Colyseus client SDK for React, connection methods, room events, and messaging. Use when connecting to multiplayer server.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Colyseus client SDK for React, connection methods, room events, and messaging. Use when connecting to multiplayer server.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Complete Developer workflow orchestration - task research sequence, implementation flow, validation gates, PRD synchronization, exit conditions.
Complete Game Designer workflow - skill invocation protocol, GDD creation, playtest flow with GDD review, design sessions. MUST load before starting assignments.
Complete PM Coordinator workflow - task assignment, project orchestration, PRD management, worker coordination. Use proactively when starting PM agent work.
Complete PM Coordinator workflow - task assignment, project orchestration, PRD management, worker coordination. Use proactively when starting PM agent work.
Complete QA Validator workflow orchestration. References specialized skills for each validation step. Load at session startup for full protocol.
Base instructions and guidelines for all agents in the system. This skill provides foundational behaviors and communication protocols that all agents should follow.
| name | dev-multiplayer-colyseus-client |
| description | Colyseus client SDK for React, connection methods, room events, and messaging. Use when connecting to multiplayer server. |
| category | multiplayer |
Connect to Colyseus server from React applications with colyseus.js.
Use when:
import { Client, Room } from 'colyseus.js';
const client = new Client('ws://localhost:2567');
async function joinGame() {
try {
const room = await client.joinOrCreate('game_room', {});
room.onStateChange((state) => {
console.log('New state:', state);
});
room.onMessage('game_event', (data) => {
console.log('Received:', data);
});
room.onLeave((code) => {
console.log('Left room:', code);
});
} catch (e) {
console.error('Join error:', e);
}
}
// Join existing or create new
const room = await client.joinOrCreate('room_name', { options: 'value' });
// Create new (fails if exists)
const room = await client.create('room_name', { options: 'value' });
// Join existing only (fails if full/not found)
const room = await client.join('room_name', { options: 'value' });
// Join by specific room ID
const room = await client.joinById('room_id_here', { options: 'value' });
// Reconnect to previous room
const room = await client.reconnect(reconnectionToken);
// Consume seat reservation
const room = await client.consumeSeatReservation(reservation);
import { useEffect, useRef, useState } from 'react';
import { Client, Room } from 'colyseus.js';
const client = new Client('ws://localhost:2567');
function GameRoom() {
const roomRef = useRef<Room>();
const [isConnecting, setIsConnecting] = useState(true);
const [players, setPlayers] = useState([]);
useEffect(() => {
const req = client.joinOrCreate('my_room', {});
req.then((room) => {
roomRef.current = room;
setIsConnecting(false);
room.onStateChange((state) => setPlayers(state.players.toJSON()));
});
return () => {
req.then((room) => room.leave());
};
}, []);
return (
<div>
{isConnecting ? 'Connecting...' :
players.map((player) => (
<div key={player.id}>{player.name}</div>
))
}
</div>
);
}
// Properties
room.state // Current room state (Schema instance)
room.sessionId // Unique client identifier
room.id // Room identifier (shareable)
room.name // Room handler name
// Send message to server
room.send('message_type', { data: 'value' });
// Send raw bytes
room.sendBytes(0, [0x01, 0x02, 0x03]);
// Leave room
room.leave(); // Consented leave
room.leave(false); // Forced leave
// Remove all listeners
room.removeAllListeners();
// Message events
room.onMessage('message_type', (data) => {
console.log('Received:', data);
});
// State change events
room.onStateChange((state) => {
console.log('State updated:', state.toJSON());
});
// One-time state change (first state only)
room.onStateChange.once((state) => {
console.log('Initial state:', state);
});
// Leave event
room.onLeave((code) => {
console.log('Left room with code:', code);
// 1000 = normal shutdown
// 4000-4999 = custom codes
});
// Error event
room.onError((code, message) => {
console.error('Room error:', code, message);
});
// GET request
client.http.get('/api/endpoint').then(response => {
console.log(response.data);
});
// POST request
client.http.post('/api/endpoint', { body: 'data' }).then(response => {
console.log(response.data);
});
// PUT request
client.http.put('/api/endpoint', { body: 'data' }).then(response => {
console.log(response.data);
});
// DELETE request
client.http.delete('/api/endpoint').then(response => {
console.log(response.data);
});
// RoomContext.tsx
import React, { createContext, useContext, useState, useEffect } from 'react';
import { Client, Room } from 'colyseus.js';
interface RoomContextType {
room: Room | null;
state: any;
isConnected: boolean;
join: () => void;
}
export const RoomContext = createContext<RoomContextType>({} as RoomContextType);
export function useRoom() {
return useContext(RoomContext);
}
export function RoomProvider({ children }: { children: React.ReactNode }) {
const [room, setRoom] = useState<Room | null>(null);
const [state, setState] = useState<any>(null);
const [isConnected, setIsConnected] = useState(false);
const join = async () => {
try {
const client = new Client('ws://localhost:2567');
const joinedRoom = await client.joinOrCreate('game_room');
setRoom(joinedRoom);
setIsConnected(true);
joinedRoom.onStateChange((newState) => {
setState(newState.toJSON());
});
joinedRoom.onLeave(() => {
setIsConnected(false);
setRoom(null);
});
} catch (e) {
console.error('Failed to join:', e);
}
};
return (
<RoomContext.Provider value={{ room, state, isConnected, join }}>
{children}
</RoomContext.Provider>
);
}
// Anonymous sign-in
client.auth.signInAnonymously()
.then((response) => {
console.log('Authenticated:', response.user);
})
.catch((error) => {
console.error('Auth error:', error);
});
// Listen to auth changes
client.auth.onChange((authData) => {
if (authData.token) {
console.log('User logged in:', authData.user);
} else {
console.log('User logged out');
}
});
// Auth tokens sent automatically with HTTP requests
client.http.get('/profile').then(response => {
// Authorization header included
});
CRITICAL: Handle all connection states properly for a good UX.
// Connection states enum
enum ConnectionState {
DISCONNECTED = 'disconnected',
CONNECTING = 'connecting',
CONNECTED = 'connected',
RECONNECTING = 'reconnecting',
ERROR = 'error',
}
// Connection hook with lifecycle
function useColyseusConnection(roomName: string) {
const [state, setState] = useState<ConnectionState>(ConnectionState.DISCONNECTED);
const [room, setRoom] = useState<Room | null>(null);
const [error, setError] = useState<string | null>(null);
const connect = async () => {
setState(ConnectionState.CONNECTING);
setError(null);
try {
const client = new Client('ws://localhost:2567');
const joinedRoom = await client.joinOrCreate(roomName);
setRoom(joinedRoom);
setState(ConnectionState.CONNECTED);
// Handle disconnection
joinedRoom.onLeave((code) => {
if (code === 1000) {
setState(ConnectionState.DISCONNECTED);
} else {
setState(ConnectionState.RECONNECTING);
// Attempt reconnection
setTimeout(() => connect(), 3000);
}
});
joinedRoom.onError((err) => {
setError(err.message);
setState(ConnectionState.ERROR);
});
} catch (err) {
setError(err.message);
setState(ConnectionState.ERROR);
}
};
const disconnect = () => {
if (room) {
room.leave();
setRoom(null);
setState(ConnectionState.DISCONNECTED);
}
};
return { state, error, connect, disconnect, room };
}
For E2E tests covering disconnect/reconnect cycles:
// E2E test for reconnection
test('handles disconnect and reconnect', async ({ page }) => {
// 1. Connect to server
await page.goto('/game');
await expect(page.getByTestId('connection-status')).toHaveText('Connected');
// 2. Simulate server disconnect
await page.evaluate(() => {
// @ts-ignore - Test utility
window.__testDisconnectServer();
});
// 3. Verify disconnected state
await expect(page.getByTestId('connection-status')).toHaveText('Disconnected');
// 4. Wait for auto-reconnect
await expect(page.getByTestId('connection-status')).toHaveText('Reconnecting', { timeout: 5000 });
// 5. Verify reconnected
await expect(page.getByTestId('connection-status')).toHaveText('Connected', { timeout: 10000 });
});
// Browser's online/offline events
useEffect(() => {
const handleOnline = () => {
console.log('Browser online - attempt reconnect');
// Trigger reconnection logic
};
const handleOffline = () => {
console.log('Browser offline - show disconnected');
setState(ConnectionState.DISCONNECTED);
};
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
| ❌ Wrong | ✅ Right |
|---|---|
| Not leaving room on unmount | Always room.leave() in cleanup |
| Using CommonJS imports | Use import { Client } from 'colyseus.js' |
| Client sends absolute position | Client sends input (WASD, aim) |
| Ignoring error events | Always handle onError |