Refactor client packet decoding

Incoming client packets are now decoded into structs, rather than `map[string]interface{}`. This allows handlers to defer unmarshaling opaque fields (e.g., ping data) via `json.RawMessage`.

Go's JSON decoder uses case-insensitive matching for struct field names even if an explicit `json:` tag is specified. This invalidates two of the `TestHeloCase()` tests, but clients should still assume that field names and values are case-sensitive.