Create a new packet analyzer for Minecraft Bedrock logs. Generates template code, provides documentation links, and guides testing workflow.
apm install @mc-zuri/create-analyzer[](https://apm-p1ls2dz87-atlamors-projects.vercel.app/packages/@mc-zuri/create-analyzer)---
name: create-analyzer
description: Create a new packet analyzer for Minecraft Bedrock logs. Generates template code, provides documentation links, and guides testing workflow.
allowed-tools: Read, Write, Glob, Grep, Bash
---
# Create Minecraft Bedrock Packet Analyzer
Generate a new domain-specific packet analyzer for analyzing captured Bedrock packets.
## Quick Start
1. **Identify the domain**: inventory, movement, entities, chat, blocks, etc.
2. **Find relevant packets**: Search `protocol.d.ts` for packet types
3. **Generate analyzer file** using template below
4. **Export from index.ts**
5. **Test with captured logs**
## Analyzer Template
Create file at: `packages/minecraft-logs-analyzers/src/analyzers/{domain}.ts`
```typescript
import { BaseAnalyzer } from "../base-analyzer.ts";
import type { Direction, LogEntry, AnalyzerConfig } from "../types.ts";
const PACKETS_TO_LOG = [
// Add packet names from protocol.d.ts
];
/**
* Analyzer for {domain}-related packets.
*/
export class {Domain}Analyzer extends BaseAnalyzer {
readonly config: AnalyzerConfig = {
name: "{domain}",
packets: PACKETS_TO_LOG,
};
constructor(basePath: string, registry?: any) {
super(basePath);
if (registry) this.registry = registry;
this.init();
}
protected extractFields(
direction: Direction,
name: string,
packet: any
): LogEntry | null {
const base = this.createBaseEntry(direction, name);
switch (name) {
// Add case for each packet type
// case "packet_name":
// return { ...base, field: packet.field };
default:
return null;
}
}
}
```
## Registration
Add export to `packages/minecraft-logs-analyzers/src/index.ts`:
```typescript
export { {Domain}Analyzer } from "./analyzers/{domain}.ts";
```
## Documentation Locations
| Resource | Location |
|----------|----------|
| Protocol types | `packages/mineflayer-bedrock/src/protocol.d.ts` |
| Existing analyzers | `packages/minecraft-logs-analyzers/src/analyzers/` |
| Base class | `packages/minecraft-logs-analyzers/src/base-analyzer.ts` |
| Types | `packages/minecraft-logs-analyzers/src/types.ts` |
| Bedrock plugins | `packages/mineflayer/lib/bedrockPlugins/` |
## Finding Relevant Packets
```bash
# Search protocol.d.ts for packet types
grep -n "packet_" packages/mineflayer-bedrock/src/protocol.d.ts | head -50
# Find packets used by a specific plugin
grep -n "client.on" packages/mineflayer/lib/bedrockPlugins/*.mts
# Search for specific packet handling
grep -rn "move_player" packages/mineflayer/lib/bedrockPlugins/
```
## Common Packet Domains
| Domain | Key Packets |
|--------|-------------|
| Inventory | `inventory_slot`, `inventory_content`, `inventory_transaction`, `item_stack_request`, `item_stack_response`, `mob_equipment` |
| Movement | `player_auth_input`, `move_player`, `set_actor_motion`, `move_actor_absolute`, `move_actor_delta` |
| Entities | `add_entity`, `remove_entity`, `set_entity_data`, `set_entity_motion`, `add_player` |
| Chat | `text`, `command_request`, `command_output` |
| Blocks | `update_block`, `block_event`, `level_event`, `update_sub_chunk_blocks` |
| Combat | `actor_event`, `hurt_armor`, `entity_fall`, `animate` |
| World | `level_chunk`, `sub_chunk`, `network_chunk_publisher_update` |
## Testing Workflow
### 1. Capture Packets from Real Client
**Critical**: Always capture from the real Minecraft client first to understand the exact packet format.
```bash
# Start capture proxy
npm run start --workspace=minecraft-logs-recorder -- -o ./test-logs
# Connect Minecraft Bedrock to localhost:19150
# Perform the exact action you want to implement (crafting, chest interaction, etc.)
# Disconnect to save logs
```
The `.jsonl` file contains processed packets, `.bin` contains raw data for replay.
### 2. Analyze Captured Logs
```bash
# View first 20 packets
head -20 test-logs/*.jsonl
# Count packets by type
grep -o '"p":"[^"]*"' test-logs/*.jsonl | sort | uniq -c | sort -rn
# Find specific packets
grep '"p":"move_player"' test-logs/*.jsonl | head -5
```
### 3. Test Your Analyzer
```typescript
import { {Domain}Analyzer } from 'minecraft-logs-analyzers';
import { createReplayClient } from 'minecraft-logs-recorder/replay';
// Attach analyzer to replay client
const analyzer = new {Domain}Analyzer('test-output/replay');
const client = createReplayClient('test-logs/1.21.130-*.bin');
// Analyzer will log packets to test-output/replay-{domain}.jsonl
analyzer.attachToBot(client);
// Check output
// cat test-output/replay-{domain}.jsonl
```
### 4. Verify Output Format
```bash
# View analyzer output
cat test-output/replay-{domain}.jsonl | head -10
# Validate JSON
cat test-output/replay-{domain}.jsonl | jq -c '.' | head -10
```
## BaseAnalyzer Methods
| Method | Description |
|--------|-------------|
| `createBaseEntry(direction, name)` | Create log entry with t, tick, d, p fields |
| `updateTick(packet)` | Extract tick from packet.tick |
| `itemName(item)` | Resolve item name from network_id |
| `writeEntry(entry)` | Write entry to JSONL file |
| `message(msg, data?)` | Log custom debug message |
## Override Points
| Method | When to Override |
|--------|------------------|
| `shouldLog(name, packet)` | Custom filtering (e.g., only log non-empty actions) |
| `extractFields(direction, name, packet)` | Required - extract relevant fields |
## IMPORTANT: Start with Full Packets
**Always log full packet data initially**, not filtered/summarized data. This was critical for solving crafting implementation:
```typescript
// BAD: Filtering too early loses critical details
protected shouldLog(name: string, packet: unknown): boolean {
// Only log if it has crafting containers... ❌
return hasCraftingContainer;
}
// GOOD: Log everything first, filter later
protected shouldLog(name: string, packet: unknown): boolean {
return true; // ✅ See all packets first
}
// GOOD: Return full packet data in extractFields
return {
...base,
responses: p.responses, // ✅ Full data, not summarized
};
```
Real client packet captures revealed crucial details that were being filtered out:
- `craft_recipe_auto` uses `hotbar_and_inventory` container, not `crafting_input`
- `results_deprecated` action requires `result_items` array with full item data
- Stack IDs use negative request_id for chained actions
- `place` goes directly to inventory, not through cursor
**Only add filtering after you fully understand the protocol.**
## Example: Movement Analyzer
```typescript
import { BaseAnalyzer } from "../base-analyzer.ts";
import type { Direction, LogEntry, AnalyzerConfig } from "../types.ts";
const PACKETS_TO_LOG = [
"player_auth_input",
"move_player",
"set_actor_motion",
];
export class MovementAnalyzer extends BaseAnalyzer {
readonly config: AnalyzerConfig = {
name: "movement",
packets: PACKETS_TO_LOG,
};
constructor(basePath: string) {
super(basePath);
this.init();
}
protected extractFields(
direction: Direction,
name: string,
packet: any
): LogEntry | null {
const base = this.createBaseEntry(direction, name);
switch (name) {
case "player_auth_input":
this.updateTick(packet);
return {
...base,
tick: packet.tick,
pos: [packet.position?.x, packet.position?.y, packet.position?.z],
yaw: packet.yaw,
pitch: packet.pitch,
};
case "move_player":
return {
...base,
pos: [packet.position?.x, packet.position?.y, packet.position?.z],
mode: packet.mode,
onGround: packet.on_ground,
};
case "set_actor_motion":
return {
...base,
entityId: packet.runtime_entity_id,
velocity: [packet.velocity?.x, packet.velocity?.y, packet.velocity?.z],
};
default:
return null;
}
}
}
```