一键导入
dev-multiplayer-colyseus-state
Colyseus state schema definition, types, decorators, and serialization patterns. Use when defining room state.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Colyseus state schema definition, types, decorators, and serialization patterns. Use when defining room state.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | dev-multiplayer-colyseus-state |
| description | Colyseus state schema definition, types, decorators, and serialization patterns. Use when defining room state. |
| category | multiplayer |
Define efficient binary-serializable state for Colyseus rooms using @colyseus/schema.
Use when:
import { Schema, type, MapSchema, ArraySchema } from '@colyseus/schema';
// Primitive types
@type('string') // String
@type('number') // Number (float)
@type('uint8') // Unsigned 8-bit (0-255)
@type('uint16') // Unsigned 16-bit (0-65535)
@type('uint32') // Unsigned 32-bit
@type('int8') // Signed 8-bit
@type('int16') // Signed 16-bit
@type('int32') // Signed 32-bit
@type('boolean') // Boolean
@type('float32') // 32-bit float
// Collection types
@type({ map: PlayerState }) // Map<string, PlayerState>
@type([PlayerState]) // Array<PlayerState>
import { Schema, type } from '@colyseus/schema';
class PlayerState extends Schema {
@type('string') clientId: string = '';
@type('uint8') team: number = 0; // 0 = orange, 1 = blue
@type('float32') x: number = 0;
@type('float32') y: number = 0;
@type('float32') z: number = 0;
@type('float32') rotation: number = 0;
@type('uint16') score: number = 0;
@type('boolean') isAlive: boolean = true;
}
import { Schema, type, MapSchema } from '@colyseus/schema';
class GameRoomState extends Schema {
@type({ map: PlayerState }) players = new MapSchema<PlayerState>();
@type('uint8') phase: number = 0; // 0=waiting, 1=playing, 2=ended
@type('uint16') orangeScore: number = 0;
@type('uint16') blueScore: number = 0;
}
class Vector3Schema extends Schema {
@type('float32') x: number = 0;
@type('float32') y: number = 0;
@type('float32') z: number = 0;
}
class PlayerState extends Schema {
@type('string') clientId: string = '';
@type(Vector3Schema) position: Vector3Schema = new Vector3Schema();
@type(Vector3Schema) velocity: Vector3Schema = new Vector3Schema();
@type('uint16') score: number = 0;
@type('uint8') health: number = 100;
@type('uint8') inkTank: number = 100;
@type('boolean') isAlive: boolean = true;
}
class TeamScore extends Schema {
@type('uint16') paintCoverage: number = 0;
@type('uint16') kills: number = 0;
@type('uint16') deaths: number = 0;
@type('boolean') hasWon: boolean = false;
}
class MatchState extends Schema {
@type('string') phase: string = 'waiting';
@type('uint16') timeRemaining: number = 180;
@type({ map: TeamScore }) teamScores = new MapSchema<TeamScore>();
@type({ map: PlayerState }) players = new MapSchema<PlayerState>();
@type([PaintSplat]) paintSplats = new ArraySchema<PaintSplat>();
}
export class GameRoom extends Room<GameRoomState> {
onCreate(options: any) {
this.setState(new GameRoomState());
}
onJoin(client: Client, options: any) {
const player = new PlayerState();
player.clientId = client.sessionId;
player.x = 0; player.y = 0; player.z = 0;
// Assign team
const orangeCount = this.getOrangeCount();
const blueCount = this.getBlueCount();
player.team = orangeCount <= blueCount ? 0 : 1;
this.state.players.set(client.sessionId, player);
}
onLeave(client: Client, consented: boolean) {
this.state.players.delete(client.sessionId);
}
onMessage(client: Client, data: any) {
const player = this.state.players.get(client.sessionId);
if (!player) return;
// Update player state
if (data.type === 'move') {
player.x = data.x;
player.y = data.y;
player.z = data.z;
}
}
private getOrangeCount(): number {
return Array.from(this.state.players.values())
.filter(p => p.team === 0).length;
}
private getBlueCount(): number {
return Array.from(this.state.players.values())
.filter(p => p.team === 1).length;
}
}
class MyState extends Schema {
@type([PlayerState]) players = new ArraySchema<PlayerState>();
}
// Add to array
this.state.players.push(new PlayerState());
// Remove from array
this.state.players.splice(index, 1);
// Iterate
this.state.players.forEach((player, index) => {
console.log(player.clientId);
});
| Use Case | Type | Bytes | Range |
|---|---|---|---|
| Player health 0-100 | uint8 | 1 | 0-255 |
| Score 0-65535 | uint16 | 2 | 0-65535 |
| Coordinates (-100 to 100) | float32 | 4 | ±3.4E38 |
| Team enum | uint8 | 1 | 0-255 |
| Player ID | string | variable | text |
| Boolean flag | boolean | 1 | true/false |
| ❌ Wrong | ✅ Right |
|---|---|
| Missing @type decorator | Always add @type('string') |
Using number for small ranges | Use uint8, uint16 for savings |
| Deep nesting (4+ levels) | Keep state shallow |
| Not initializing defaults | Set default: x: number = 0 |
From arch-003 retrospective - proven patterns for @colyseus/schema.
import { Schema, type } from '@colyseus/schema';
/**
* Player state schema for server-authoritative multiplayer
*
* Type Selection Guidelines:
* - uint8: 0-255 (health, armor, small counters)
* - uint16: 0-65535 (scores, larger counters)
* - float32: Coordinates, rotation (precision needed)
* - string: Variable text (sessionId, weapon names)
* - boolean: Flags (isAlive, connected)
*/
export class PlayerState extends Schema {
// Position (float32 for precision)
@type('float32') x: number = 0;
@type('float32') y: number = 0;
@type('float32') z: number = 0;
// Rotation (degrees or radians)
@type('float32') rotation: number = 0;
// Combat stats (uint8 sufficient for 0-100 ranges)
@type('uint8') health: number = 100;
@type('uint8') armor: number = 0;
// Weapon (string for flexibility - consider enum for type safety)
@type('string') weapon: string = 'blaster';
// Score tracking
@type('uint8') kills: number = 0;
@type('boolean') isAlive: boolean = true;
}
import { Schema, type, MapSchema } from '@colyseus/schema';
import { PlayerState } from './PlayerState';
export class ArenaState extends Schema {
// Players map - keyed by sessionId
@type({ map: PlayerState }) players = new MapSchema<PlayerState>();
// Room settings
@type('string') mapSeed: string = '';
// Match state
@type('uint16') playersAlive: number = 0;
@type('string') phase: string = 'lobby'; // lobby, playing, ended
}
| Data | Type | Bytes | Range | Example |
|---|---|---|---|---|
| Health 0-100 | uint8 | 1 | 0-255 | @type('uint8') health: number = 100 |
| Armor 0-100 | uint8 | 1 | 0-255 | @type('uint8') armor: number = 0 |
| Kills 0-63 | uint8 | 1 | 0-255 | @type('uint8') kills: number = 0 |
| Score 0-65535 | uint16 | 2 | 0-65535 | @type('uint16') score: number = 0 |
| Position X/Y/Z | float32 | 4 | ±3.4E38 | @type('float32') x: number = 0 |
| Rotation | float32 | 4 | ±3.4E38 | @type('float32') rotation: number = 0 |
| Session ID | string | variable | text | @type('string') sessionId: string = '' |
| Weapon name | string | variable | text | @type('string') weapon: string = '' |
| Alive status | boolean | 1 | true/false | @type('boolean') isAlive: boolean = true |
| Player map | MapSchema | 4+ bytes | dynamic | @type({ map: PlayerState }) |
| ❌ Wrong | ✅ Right | Why |
|---|---|---|
@type() missing | @type('uint8') | Required for serialization |
@type('number') for health | @type('uint8') | Saves 3 bytes per player |
health without default | health: number = 100 | Prevents undefined |
Using Array for players | MapSchema<Player> | Efficient lookup by ID |
Sources:
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.