do
    -- Protocol
    local p_foo = Proto("foo", "FOO Protocol")

    -- Preferences
    local foo_settings = {
        use_stringz = false
    }

    p_foo.prefs.use_stringz = Pref.bool("Use stringz", foo_settings.use_stringz,
        "Whether to use stringz() or strsize() for strings.")

    local TYPE_PING = 1
    local TYPE_PONG = 2

    -- Value Strings
    local foo_type_vals = {
        [TYPE_PING] = "Ping",
        [TYPE_PONG] = "Pong"
    }

    -- Fields
    local pf = {
        hdr = ProtoField.bytes("foo.hdr", "Foo Header", base.NONE),
        hdr_magic = ProtoField.uint32("foo.hdr.magic", "Magic", base.HEX),
        hdr_type = ProtoField.uint16("foo.hdr.type", "Type", base.DEC, foo_type_vals),
        hdr_len = ProtoField.uint16("foo.hdr.len", "Length", base.DEC),
        msg_len = ProtoField.uint16("foo.msg_len", "Message Length", base.DEC),
        msg = ProtoField.string("foo.msg", "Message", base.ASCII),
        data = ProtoField.bytes("foo.data", "Data", base.NONE)
    }

    p_foo.fields = pf

    -- Dissection
    function p_foo.dissector(tvbuf, pinfo, tree)
        local foo_tree = tree:add(p_foo, tvbuf(0, -1))
        local hdr_tree
        local msg_type
        local length
        local str, str_len

        pinfo.cols.protocol:set("FOO")

        hdr_tree = foo_tree:add(pf.hdr, tvbuf(0, 8))
        hdr_tree:add(pf.hdr_magic, tvbuf(0, 4))
        hdr_tree:add(pf.hdr_type, tvbuf(4, 2))
        hdr_tree:add(pf.hdr_len, tvbuf(6, 2))

        msg_type = tvbuf(4, 2):uint()
        length = tvbuf(6, 2):uint()
        pinfo.cols.info:set(foo_type_vals[msg_type] or "Unknown")

        if msg_type == TYPE_PING or msg_type == TYPE_PONG then
            if p_foo.prefs.use_stringz == true then
                str = tvbuf(8):stringz(ENC_ASCII)
                str_len = str:len()
            else
                str_len = tvbuf(8):strsize(ENC_ASCII) - 1
                str = tvbuf(8, str_len + 1):string()
            end

            foo_tree:add(pf.msg_len, str_len + 1):set_generated(true)
            foo_tree:add(pf.msg, tvbuf(8, str_len + 1))
            pinfo.cols.info:append(": " .. str)
        else
            foo_tree:add(pf.data, tvbuf(8, length))
        end
    end

    -- Registration
    local udp_table = DissectorTable.get("udp.port")
    udp_table:add(33333, p_foo)
end
