# Adding New Packet Parsers This guide explains how to implement parsers for new GCNet packet types. ## Architecture Overview The packet parsing system uses several design patterns: ``` ┌─────────────────────────────────────────────────────────────────┐ │ GCNetPacketProcessor │ │ ┌─────────────────────────────────────────────────────────────┐│ │ │ PacketParserFactory (Factory) ││ │ │ ┌────────────┐ ┌────────────┐ ┌──────────────────────────┐ ││ │ │ │ PingParser │ │ PongParser │ │ VerifyAccountRequestParser│ ││ │ │ └────────────┘ └────────────┘ └──────────────────────────┘ ││ │ │ Strategy Pattern ││ │ └─────────────────────────────────────────────────────────────┘│ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ PacketContext │ │ • decrypted payload bytes │ │ • TCP segment (src/dst IP:port) │ │ • server port for direction detection │ └─────────────────────────────────────────────────────────────────┘ ``` ### Design Patterns Used | Pattern | Class | Purpose | |---------|-------|---------| | **Strategy** | `PacketParser` interface | Each opcode has its own parsing strategy | | **Factory** | `PacketParserFactory` | Creates the correct parser for each opcode | | **Context** | `PacketContext` | Immutable context with payload + connection metadata | | **Registry** | `GCNetOpcode` enum | Maps opcode numbers to metadata and direction hints | | **Null Object** | `GenericPayloadParser` | Fallback for unimplemented opcodes | ## Step-by-Step: Adding a New Parser ### 1. Define the Opcode Add the opcode to `GCNetOpcode.java`: ```java // In packets.com.gcpp.GCNetOpcode /** * Example: Player join request. * Structure: Player name (string), Character class (int), Level (short) */ ENU_PLAYER_JOIN_REQ(50, "ENU_PLAYER_JOIN_REQ", "Player join request", Direction.CLIENT_TO_SERVER), ``` The `Direction` enum indicates expected traffic flow: - `CLIENT_TO_SERVER` — Sent from client **to** server port (dstPort == serverPort) - `SERVER_TO_CLIENT` — Sent from server **from** server port (srcPort == serverPort) - `BIDIRECTIONAL` — Valid both ways (e.g., PING/PONG) ### 2. Create the Parser Create a new class in `packets/parsers/`: ```java package com.gcnet.decryptor.packets.parsers; import packets.com.gcemu.gcpp.GCNetOpcode; import packets.com.gcemu.gcpp.PacketContext; import packets.com.gcemu.gcpp.PacketParser; import java.nio.ByteBuffer; import java.util.LinkedHashMap; import java.util.Map; /** * Parser for ENU_PLAYER_JOIN_REQ packets (opcode 50). * *
| Offset | Size | Type | Description |
|---|---|---|---|
| 0 | 2 | ushort (BE) | Opcode (50) |
| 2 | 4 | int (BE) | Content size |
| 6 | 1 | bool | Compression flag |
| 7 | 4 | int (LE) | Player name byte length |
| 11 | var | string (UTF-16LE) | Player name |
| var | 4 | int (LE) | Character class ID |
| var | 2 | short (LE) | Character level |
Direction: {@link GCNetOpcode.Direction#CLIENT_TO_SERVER}
*/ @PacketParser.PacketParserFor(opcode = 50) public class PlayerJoinRequestParser implements PacketParser { private static final int CONTENT_OFFSET = 7; @Override public ParseResult parse(PacketContext context) { byte[] payload = context.decryptedPayload(); if (payload.length < CONTENT_OFFSET + 10) { return ParseResult.empty(GCNetOpcode.ENU_PLAYER_JOIN_REQ, "Client → Server"); } int offset = CONTENT_OFFSET; Map