Claude-skill-registry communication-protocols
Game server communication protocols including gRPC, REST, and custom binary protocols
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/communication-protocols" ~/.claude/skills/majiayu000-claude-skill-registry-communication-protocols && rm -rf "$T"
manifest:
skills/data/communication-protocols/SKILL.mdsource content
Communication Protocols for Game Servers
Implement efficient communication protocols between game services and clients.
Protocol Selection Guide
| Protocol | Latency | Throughput | Use Case |
|---|---|---|---|
| Custom Binary | Lowest | Highest | Real-time gameplay |
| gRPC | Low | High | Service-to-service |
| WebSocket | Low | Medium | Browser clients |
| REST | Medium | Medium | Admin APIs, lobbies |
| QUIC | Low | High | Mobile, unreliable networks |
gRPC for Game Services
// matchmaking.proto syntax = "proto3"; package game.matchmaking; service Matchmaking { rpc FindMatch(MatchRequest) returns (MatchResponse); rpc JoinQueue(QueueRequest) returns (stream QueueUpdate); rpc CancelQueue(CancelRequest) returns (CancelResponse); } message MatchRequest { string player_id = 1; string game_mode = 2; int32 skill_rating = 3; repeated string preferred_regions = 4; } message MatchResponse { string match_id = 1; string server_address = 2; int32 server_port = 3; repeated TeamAssignment teams = 4; string connection_token = 5; } message QueueUpdate { enum Status { SEARCHING = 0; MATCH_FOUND = 1; CANCELLED = 2; } Status status = 1; int32 estimated_wait_seconds = 2; int32 players_in_queue = 3; }
Go gRPC Server
type matchmakingServer struct { pb.UnimplementedMatchmakingServer matchmaker *Matchmaker } func (s *matchmakingServer) FindMatch( ctx context.Context, req *pb.MatchRequest, ) (*pb.MatchResponse, error) { match, err := s.matchmaker.FindMatch(ctx, req.PlayerId, req.GameMode, req.SkillRating) if err != nil { return nil, status.Errorf(codes.Internal, "matchmaking failed: %v", err) } return &pb.MatchResponse{ MatchId: match.ID, ServerAddress: match.ServerAddr, ServerPort: int32(match.ServerPort), ConnectionToken: match.Token, }, nil } func (s *matchmakingServer) JoinQueue( req *pb.QueueRequest, stream pb.Matchmaking_JoinQueueServer, ) error { updates := s.matchmaker.Subscribe(req.PlayerId) defer s.matchmaker.Unsubscribe(req.PlayerId) for update := range updates { if err := stream.Send(update); err != nil { return err } if update.Status == pb.QueueUpdate_MATCH_FOUND { return nil } } return nil }
Custom Binary Protocol
// Packet header (8 bytes) struct PacketHeader { uint8_t type; // Message type uint8_t flags; // Compression, reliability flags uint16_t length; // Payload length uint32_t sequence; // Packet sequence for ordering/ack }; enum PacketType : uint8_t { PLAYER_INPUT = 0x01, STATE_UPDATE = 0x02, PLAYER_JOIN = 0x03, PLAYER_LEAVE = 0x04, CHAT_MESSAGE = 0x10, PING = 0xFE, PONG = 0xFF }; enum PacketFlags : uint8_t { FLAG_RELIABLE = 0x01, FLAG_COMPRESSED = 0x02, FLAG_ENCRYPTED = 0x04 }; // Zero-copy packet builder class PacketBuilder { uint8_t buffer[MAX_PACKET_SIZE]; size_t offset = sizeof(PacketHeader); public: PacketBuilder& writeU8(uint8_t v) { buffer[offset++] = v; return *this; } PacketBuilder& writeU16(uint16_t v) { *reinterpret_cast<uint16_t*>(&buffer[offset]) = htons(v); offset += 2; return *this; } PacketBuilder& writeFloat(float v) { *reinterpret_cast<float*>(&buffer[offset]) = v; offset += 4; return *this; } std::span<uint8_t> build(PacketType type, uint8_t flags = 0) { auto* header = reinterpret_cast<PacketHeader*>(buffer); header->type = static_cast<uint8_t>(type); header->flags = flags; header->length = htons(offset - sizeof(PacketHeader)); header->sequence = htonl(nextSequence++); return {buffer, offset}; } }; // Player input packet (compact) struct PlayerInputPacket { uint32_t tick; // 4 bytes uint8_t keys; // 1 byte: WASD + jump + fire (bitfield) int16_t aim_x; // 2 bytes: quantized aim [-32768, 32767] int16_t aim_y; // 2 bytes: quantized aim }; // Total: 9 bytes
WebSocket for Browser Games
// Server (Node.js with ws) const WebSocket = require('ws'); const wss = new WebSocket.Server({ port: 8080, perMessageDeflate: true, // Compression maxPayload: 64 * 1024 // 64KB limit }); wss.on('connection', (ws, req) => { const playerId = authenticate(req); ws.on('message', (data, isBinary) => { if (isBinary) { // Binary protocol for gameplay const view = new DataView(data.buffer); const type = view.getUint8(0); handleBinaryMessage(playerId, type, view); } else { // JSON for lobby/chat const msg = JSON.parse(data); handleJsonMessage(playerId, msg); } }); ws.on('close', () => { onPlayerDisconnect(playerId); }); // Send binary state updates at 60Hz const tickInterval = setInterval(() => { if (ws.readyState === WebSocket.OPEN) { const state = serializeGameState(playerId); ws.send(state, { binary: true }); } }, 16); ws.on('close', () => clearInterval(tickInterval)); }); // Client class GameClient { constructor(url) { this.ws = new WebSocket(url); this.ws.binaryType = 'arraybuffer'; this.ws.onmessage = (event) => { if (event.data instanceof ArrayBuffer) { this.handleStateUpdate(new DataView(event.data)); } else { this.handleJsonMessage(JSON.parse(event.data)); } }; } sendInput(keys, aimX, aimY) { const buffer = new ArrayBuffer(9); const view = new DataView(buffer); view.setUint32(0, this.currentTick); view.setUint8(4, keys); view.setInt16(5, aimX); view.setInt16(7, aimY); this.ws.send(buffer); } }
REST API for Game Services
// Lobby API with proper error handling type LobbyHandler struct { lobbyService *LobbyService } func (h *LobbyHandler) CreateLobby(w http.ResponseWriter, r *http.Request) { var req CreateLobbyRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { respondError(w, http.StatusBadRequest, "invalid request body") return } lobby, err := h.lobbyService.Create(r.Context(), req) if err != nil { switch { case errors.Is(err, ErrPlayerAlreadyInLobby): respondError(w, http.StatusConflict, err.Error()) case errors.Is(err, ErrMaxLobbiesReached): respondError(w, http.StatusTooManyRequests, err.Error()) default: respondError(w, http.StatusInternalServerError, "internal error") } return } respondJSON(w, http.StatusCreated, lobby) } func respondJSON(w http.ResponseWriter, status int, data interface{}) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) json.NewEncoder(w).Encode(data) }
Protocol Selection Matrix
| Scenario | Protocol | Reason |
|---|---|---|
| Real-time gameplay | Custom UDP binary | Lowest latency |
| Microservices | gRPC | Type safety, streaming |
| Web/mobile lobby | WebSocket JSON | Browser compatibility |
| Admin dashboard | REST | Standard tooling |
| Streaming updates | gRPC streaming | Backpressure handling |
Troubleshooting
Common Failure Modes
| Error | Root Cause | Solution |
|---|---|---|
| Connection reset | Message too large | Chunk large messages |
| Timeout | Slow processing | Async handlers |
| Parse error | Version mismatch | Protocol versioning |
| High latency | No compression | Enable compression |
Debug Checklist
# gRPC debugging GRPC_VERBOSITY=DEBUG GRPC_TRACE=all ./game-server # WebSocket inspection wscat -c ws://localhost:8080 # Protocol buffer decoding protoc --decode=game.StateUpdate game.proto < message.bin # Network trace tcpdump -i any port 8080 -w capture.pcap
Unit Test Template
func TestMatchmakingRPC(t *testing.T) { server := setupTestServer() defer server.Stop() conn, err := grpc.Dial(server.Addr, grpc.WithInsecure()) require.NoError(t, err) defer conn.Close() client := pb.NewMatchmakingClient(conn) resp, err := client.FindMatch(context.Background(), &pb.MatchRequest{ PlayerId: "player123", GameMode: "ranked", SkillRating: 1500, }) require.NoError(t, err) assert.NotEmpty(t, resp.MatchId) assert.NotEmpty(t, resp.ServerAddress) } func TestBinaryProtocol(t *testing.T) { builder := NewPacketBuilder() packet := builder. WriteU32(12345). // tick WriteU8(0x0F). // keys WriteI16(1000). // aim_x WriteI16(-500). // aim_y Build(PLAYER_INPUT) parsed := ParsePlayerInput(packet) assert.Equal(t, uint32(12345), parsed.Tick) assert.Equal(t, uint8(0x0F), parsed.Keys) }
Resources
- Protocol templatesassets/
- Performance benchmarksreferences/