private function processChatMessage()
    {
        $length = $this->readBody('l', 4);
        if ($length <= 0) {
            return;
        }
        $chat = $this->readBodyRaw($length);

        // Chat messages are stored as "@#%dPlayerName: Message", where %d is a
        // digit from 1 to 8 indicating player's index (or colour).
        if ($chat[0] == '@' && $chat[1] == '#' && $chat[2] >= '1' && $chat[2] <= '8') {
            $chat = rtrim($chat); // Remove null terminator
            if (substr($chat, 3, 2) == '--' && substr($chat, -2) == '--') {
                // Skip messages like "--Warning: You are under attack... --"
                return;
            } else if (!empty($this->playersByNumber[$chat[2]])) {
                $player = $this->playersByNumber[$chat[2]];
            } else {
                // Shouldn't happen, but we'll let the ChatMessage factory
                // create a fake player for this message.
                // TODO that auto-create behaviour is probably not desirable…
                $player = null;
            }
            $this->chatMessages[] = ChatMessage::create(
                $this->currentTime,
                $player,
                substr($chat, 3)
            );
        }
    }