Skip to main content

rewriter

Attribute Macro rewriter 

Source
#[rewriter]
Expand description

Implements the DemoRewriter trait for your struct.

Apply this to an inherent impl block and mark methods with writer callback attributes. The macro builds the RewriteInterests mask from those methods so the writer only decodes the parts of the replay your rewrite needs.

§Callback Types

  • #[rewrite_demo_message] sees outer EDemoCommands payloads before packet-level decoding.
  • #[rewrite_packet_message] sees messages inside demo packets. It can take raw (msg_type, payload) arguments or a decoded protobuf message.
  • #[rewrite_packet_messages] can mutate the final decoded packet message list after individual packet-message callbacks run.
  • #[rewrite_demo_string_tables], #[rewrite_string_table_entry], #[rewrite_svc_create_string_table], and #[rewrite_svc_update_string_table] target string-table data at different levels of decoding.
  • #[rewrite_field] and #[replace_entity_field] replace decoded entity field values, while #[should_rewrite_entity] filters which entities enter that path.

§Return Values

Message callbacks return Result<MessageRewrite, ParserError>. Keep leaves the current payload alone, Drop removes it, Replace(bytes) writes explicit bytes, and Rewrite re-encodes a mutable decoded protobuf argument when that callback supports it. List, entry, and filter callbacks use the return type of the matching DemoRewriter method.

§Examples

§Drop a decoded packet message

struct RemoveChat;

#[rewriter]
impl RemoveChat {
    #[rewrite_packet_message]
    fn remove_chat(
        &mut self,
        _msg: CDotaUserMsgChatMessage,
    ) -> Result<MessageRewrite, ParserError> {
        Ok(MessageRewrite::Drop)
    }
}

§Mutate and re-encode a decoded packet message

struct RedactChat;

#[rewriter]
impl RedactChat {
    #[rewrite_packet_message]
    fn redact_chat(
        &mut self,
        message: &mut CDotaUserMsgChatMessage,
    ) -> Result<MessageRewrite, ParserError> {
        message.message_text = Some("[redacted]".to_string());
        Ok(MessageRewrite::Rewrite)
    }
}

§Rewrite a string table entry

struct AnonymizeUserInfo;

#[rewriter]
impl AnonymizeUserInfo {
    #[rewrite_string_table_entry]
    fn rewrite_userinfo(
        &mut self,
        table_name: &str,
        entry: &mut StringTableEntryUpdate,
    ) -> Result<(), ParserError> {
        if table_name == "userinfo" {
            if let Some(value) = entry.value_mut() {
                let mut player = CMsgPlayerInfo::decode(value.as_slice())?;
                player.name = Some("Anonymous".to_string());
                *value = player.encode_to_vec();
            }
        }
        Ok(())
    }
}

§Rewrite decoded entity fields

struct RemoveSteamIds;

#[rewriter]
impl RemoveSteamIds {
    #[rewrite_field(class = "CDOTA_PlayerResource", field = ends_with("m_iPlayerSteamID"))]
    fn remove_steam_id(&mut self, _value: u64) -> u64 {
        0
    }
}