├── .coveragerc ├── .gitignore ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── README.rst ├── demoparser ├── __init__.py ├── _setup_hooks.py ├── bitbuffer.pxd ├── bitbuffer.pyx ├── bytebuffer.py ├── consts.py ├── demofile.pyx ├── entities │ ├── __init__.py │ ├── base.py │ ├── entity_list.py │ ├── game_rules.py │ ├── player.py │ ├── team.py │ └── weapon.py ├── examples │ ├── chat.py │ └── player_death.py ├── fields.py ├── props.pxd ├── props.pyx ├── protobufs │ ├── LICENSE │ ├── __init__.py │ ├── base_gcmessages.proto │ ├── cstrike15_gcmessages.proto │ ├── cstrike15_usermessages.proto │ ├── econ_gcmessages.proto │ ├── engine_gcmessages.proto │ ├── fatdemo.proto │ ├── gcsdk_gcmessages.proto │ ├── gcsystemmsgs.proto │ ├── netmessages.proto │ ├── network_connection.proto │ ├── steamdatagram_messages.proto │ ├── steammessages.proto │ └── uifontfile_format.proto ├── structures.py ├── util.pxd └── util.pyx ├── doc ├── Makefile └── source │ ├── api │ ├── demoparser.entities.rst │ ├── demoparser.rst │ └── modules.rst │ ├── conf.py │ ├── demopacket.rst │ ├── events.rst │ ├── game_events.rst │ ├── index.rst │ ├── intro.rst │ ├── usage.rst │ └── user_messages.rst ├── requirements.txt ├── setup.cfg ├── setup.py ├── test-requirements.txt ├── tests ├── test_bitbuffer.py ├── test_bytebuffer.py └── test_demofile.py └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | plugins = Cython.Coverage 3 | omit = demoparser/protobufs/* 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.egg-info 3 | __pycache__ 4 | .eggs 5 | .tox 6 | *.rar 7 | *.dem 8 | AUTHORS 9 | ChangeLog 10 | *_pb2.py 11 | *.c 12 | *.h 13 | *.so 14 | doc/_build 15 | .cache 16 | .coverage 17 | build 18 | dist 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | python: 4 | - "3.5" 5 | - "3.6" 6 | 7 | addons: 8 | apt: 9 | sources: 10 | # travis runs ubuntu 14.04 which ships with protobuf 2.5. 11 | # Python 3 support wasn't included until 2.6 12 | - sourceline: ppa:5-james-t/protobuf-ppa 13 | packages: 14 | - protobuf-compiler 15 | - python-protobuf 16 | - libprotobuf-dev 17 | 18 | install: 19 | - protoc -I . -I /usr/include --python_out . ./demoparser/protobufs/*.proto 20 | - pip install cython tox-travis 21 | 22 | script: tox 23 | 24 | stages: 25 | - lint 26 | - test 27 | 28 | jobs: 29 | include: 30 | - stage: lint 31 | script: tox -e lint 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include tox.ini 3 | recursive-include demoparser *.pyx 4 | recursive-include demoparser *.pxd 5 | prune demoparser/examples 6 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ``csgo-demoparser`` is a library for parsing CS:GO demo files. 2 | 3 | As the file is processed events are emitted for which callbacks can 4 | be registered. 5 | 6 | Quick start 7 | ----------- 8 | 9 | 1. Install:: 10 | 11 | pip install csgo-demoparser 12 | 13 | 2. Parse a demo:: 14 | 15 | >>> from demoparser.demofile import DemoFile 16 | >>> data = open('/path/to/demofile', 'rb').read() 17 | >>> df = DemoFile(data) 18 | >>> df.parse() 19 | -------------------------------------------------------------------------------- /demoparser/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibm-dev-incubator/demoparser/5e04604f421a0378b8408e32b5ce36adc7776b37/demoparser/__init__.py -------------------------------------------------------------------------------- /demoparser/_setup_hooks.py: -------------------------------------------------------------------------------- 1 | from distutils.spawn import find_executable 2 | from setuptools.command.build_py import build_py as _build_py 3 | import glob 4 | import subprocess 5 | 6 | 7 | class ProtobufBuilder(_build_py): 8 | command_name = 'build_py' 9 | 10 | def protoc(self): 11 | proto_files = glob.glob('./demoparser/protobufs/*.proto') 12 | subprocess.run([ 13 | find_executable('protoc'), 14 | '--python_out=.', 15 | '--proto_path=/usr/include/', 16 | '--proto_path=.', 17 | *proto_files 18 | ], check=True) 19 | 20 | def run(self): 21 | self.protoc() 22 | _build_py.run(self) 23 | -------------------------------------------------------------------------------- /demoparser/bitbuffer.pxd: -------------------------------------------------------------------------------- 1 | cdef class Bitbuffer: 2 | cdef unsigned int num_bytes 3 | cdef unsigned int * data 4 | cdef unsigned int index 5 | cdef unsigned int length 6 | cdef unsigned int bits_avail 7 | cdef unsigned int in_buf_word 8 | cdef bytes orig_data 9 | 10 | cpdef unsigned int next_dword(self) 11 | cpdef unsigned char read_bit(self) 12 | cpdef unsigned int read_uint_bits(self, unsigned int bits) 13 | cpdef int read_sint_bits(self, unsigned int bits) 14 | cpdef unsigned int read_var_int(self) 15 | cpdef str read_string(self, int length=*) 16 | cpdef float read_bit_normal(self) 17 | cpdef float read_bit_coord(self) 18 | cpdef float read_bit_cell_coord(self, unsigned int bits, 19 | unsigned int coord_type) 20 | cpdef bytes read_user_data(self, unsigned int bits) 21 | -------------------------------------------------------------------------------- /demoparser/bitbuffer.pyx: -------------------------------------------------------------------------------- 1 | from cython.operator cimport dereference 2 | from cpython cimport array 3 | import array 4 | from libc.math cimport ceil 5 | 6 | from demoparser import consts 7 | 8 | 9 | cdef unsigned int[33] mask_table = [ 10 | 0, 11 | ( 1 << 1 ) - 1, 12 | ( 1 << 2 ) - 1, 13 | ( 1 << 3 ) - 1, 14 | ( 1 << 4 ) - 1, 15 | ( 1 << 5 ) - 1, 16 | ( 1 << 6 ) - 1, 17 | ( 1 << 7 ) - 1, 18 | ( 1 << 8 ) - 1, 19 | ( 1 << 9 ) - 1, 20 | ( 1 << 10 ) - 1, 21 | ( 1 << 11 ) - 1, 22 | ( 1 << 12 ) - 1, 23 | ( 1 << 13 ) - 1, 24 | ( 1 << 14 ) - 1, 25 | ( 1 << 15 ) - 1, 26 | ( 1 << 16 ) - 1, 27 | ( 1 << 17 ) - 1, 28 | ( 1 << 18 ) - 1, 29 | ( 1 << 19 ) - 1, 30 | ( 1 << 20 ) - 1, 31 | ( 1 << 21 ) - 1, 32 | ( 1 << 22 ) - 1, 33 | ( 1 << 23 ) - 1, 34 | ( 1 << 24 ) - 1, 35 | ( 1 << 25 ) - 1, 36 | ( 1 << 26 ) - 1, 37 | ( 1 << 27 ) - 1, 38 | ( 1 << 28 ) - 1, 39 | ( 1 << 29 ) - 1, 40 | ( 1 << 30 ) - 1, 41 | 0x7fffffff, 42 | 0xffffffff, 43 | ] 44 | 45 | 46 | cdef class Bitbuffer: 47 | r"""Parse a stream of bits from a series of bytes. 48 | 49 | 50 | Data is cast to unsigned int* and read one unsigned int 51 | at a time. As an unsigned int is 32 bits this has the 52 | effect of reading 4 bytes at once. Reads can span integer 53 | boundaries and the consecutive integers will be merged 54 | correctly. 55 | 56 | :param data: Data to parse bit-by-bit 57 | :type data: bytes 58 | 59 | :Example: 60 | 61 | >>> buf = Bitbuffer(b'\x11\x22') 62 | >>> buf.read_uint_bits(8) 63 | 17 64 | >>> buf.read_uint_bits(8) 65 | 34 66 | """ 67 | 68 | def __init__(self, data): 69 | self.num_bytes = len(data) 70 | 71 | # We have to keep a reference to the Python object around. 72 | # It will be garbage-collected when __init__ exits otherwise. 73 | # This doesn't segfault, and we can keep reading from the pointer. 74 | # Parsing will fail far away from here though because the data 75 | # being read isn't a buffer from a demo file. 76 | self.orig_data = data 77 | 78 | # Cython does not let you cast Python objects directly so 79 | # ` data` will not work. It has to go through the 80 | # intermediate cast. 81 | self.data = data 82 | 83 | # Length in words where 1 word = 32 bits = 4 bytes 84 | self.length = int(ceil(self.num_bytes/4.0)) 85 | self.index = 0 86 | self.bits_avail = 32 87 | self.next_dword() 88 | 89 | cpdef unsigned int next_dword(self): 90 | """Move to the next 32-bit integer in the stream. 91 | 92 | :returns: unsigned int 93 | """ 94 | if self.index == self.length: 95 | self.bits_avail = 1 96 | self.in_buf_word = 0 97 | self.index += 1 98 | else: 99 | if self.index > self.length: 100 | self.in_buf_word = 0 101 | else: 102 | self.in_buf_word = self.data[self.index] 103 | self.index += 1 104 | 105 | cpdef unsigned char read_bit(self): 106 | """Read a single bit. 107 | 108 | :returns: one bit 109 | """ 110 | cdef unsigned char ret = self.in_buf_word & 1 111 | self.bits_avail -= 1 112 | 113 | if self.bits_avail == 0: 114 | self.bits_avail = 32 115 | self.next_dword() 116 | else: 117 | self.in_buf_word >>= 1 118 | 119 | return ret 120 | 121 | cpdef unsigned int read_uint_bits(self, unsigned int bits): 122 | """Read the unsigned integer represented by `bits` bits. 123 | 124 | If the number of bits remaining in the current word is 125 | not enough then the next word will be read. 126 | 127 | :param bits: Number of bits to read 128 | :type bits: unsigned int 129 | :returns: unsigned int 130 | """ 131 | cdef unsigned int ret = self.in_buf_word & mask_table[bits] 132 | 133 | if self.bits_avail >= bits: 134 | ret = self.in_buf_word & mask_table[bits] 135 | 136 | self.bits_avail -= bits 137 | if self.bits_avail: 138 | self.in_buf_word >>= bits 139 | else: 140 | self.bits_avail = 32 141 | self.next_dword() 142 | 143 | return ret 144 | else: 145 | # Merge words 146 | ret = self.in_buf_word 147 | bits -= self.bits_avail 148 | self.next_dword() 149 | 150 | ret |= ((self.in_buf_word & mask_table[bits]) << self.bits_avail) 151 | self.bits_avail = 32 - bits 152 | self.in_buf_word >>= bits 153 | 154 | return ret 155 | 156 | cpdef int read_sint_bits(self, unsigned int bits): 157 | """Read a signed integer of `bits` bits. 158 | 159 | First an unsigned integer is read then a two's complement 160 | integer is computed. 161 | 162 | :param bits: Number of bits to read 163 | :type bits: unsigned int 164 | :returns: int 165 | """ 166 | cdef unsigned int ret = self.read_uint_bits(bits) 167 | cdef unsigned int mask = 2 << (bits - 2) 168 | return -(ret & mask) + (ret & ~mask) 169 | 170 | cpdef unsigned int read_var_int(self): 171 | """Read a variable length integer. 172 | 173 | :returns: unsigned int 174 | """ 175 | cdef unsigned int num = self.read_uint_bits(6) 176 | cdef unsigned char bits = num & (16 | 32) 177 | 178 | if bits == 16: 179 | num = (num & 15) | (self.read_uint_bits(4) << 4) 180 | assert num >= 16 181 | elif bits == 32: 182 | num = (num & 15) | (self.read_uint_bits(8) << 4) 183 | assert num >= 256 184 | elif bits == 48: 185 | num = (num & 15) | (self.read_uint_bits(28) << 4) 186 | assert num >= 4096 187 | 188 | return num 189 | 190 | cpdef str read_string(self, int length=-1): 191 | r"""Read a string. 192 | 193 | If length is not provided characters are read until 194 | \\0 is reached. If length is provided then exactly 195 | length bytes will be read and the string may not be 196 | zero-terminated. 197 | 198 | :returns: str 199 | """ 200 | cdef char c 201 | cdef bint append = True 202 | cdef int index = 1 203 | ret = [] 204 | 205 | while True: 206 | c = self.read_uint_bits(8) 207 | if c == 0: 208 | append = False 209 | if length == -1: 210 | break 211 | 212 | if append: 213 | ret.append(c) 214 | if index == length: 215 | break 216 | 217 | index += 1 218 | 219 | return array.array('b', ret).tobytes().decode('utf-8') 220 | 221 | cpdef float read_bit_normal(self): 222 | cdef bint sign_bit = self.read_bit() 223 | """Read a float normal. 224 | 225 | :returns: float value between -1.0 and 1.0 226 | """ 227 | cdef int frac = self.read_uint_bits(consts.NORMAL_FRACTIONAL_BITS) 228 | cdef float value = frac * consts.NORMAL_RESOLUTION 229 | 230 | return -value if sign_bit else value 231 | 232 | cpdef float read_bit_coord(self): 233 | """Read a float and treat as a world coordinate. 234 | 235 | :returns: float 236 | """ 237 | cdef bint integer = self.read_bit() 238 | cdef bint fraction = self.read_bit() 239 | cdef bint sign_bit = 0 240 | cdef unsigned int int_val = 0 241 | cdef unsigned int frac_val = 0 242 | cdef float ret = 0.0 243 | 244 | if not integer and not fraction: 245 | return 0.0 246 | 247 | sign_bit = self.read_bit() 248 | 249 | if integer: 250 | int_val = self.read_uint_bits(consts.COORD_INTEGER_BITS) + 1 251 | 252 | if fraction: 253 | frac_val = self.read_uint_bits(consts.COORD_FRACTIONAL_BITS) 254 | 255 | value = int_val + (frac_val * consts.COORD_RESOLUTION) 256 | 257 | return -value if sign_bit else value 258 | 259 | cpdef float read_bit_cell_coord(self, unsigned int bits, 260 | unsigned int coord_type): 261 | """Read a cell coordinate. 262 | 263 | A cell coordinate is a float which has been 264 | compressed. The number of bits indicates maximum 265 | value. 266 | 267 | :param bits: number of bits to read 268 | :type bits: unsigned int 269 | :param coord_type: level of precision 270 | :type coord_type: unsigned int 271 | :returns: float 272 | """ 273 | 274 | cdef bint low_precision = (coord_type == consts.CW_LowPrecision) 275 | cdef float value = 0.0 276 | cdef float resolution = 0.0 277 | cdef unsigned int frac_bits = 0 278 | cdef unsigned int int_val, frac_val 279 | 280 | if coord_type == consts.CW_Integral: 281 | value = self.read_uint_bits(bits) 282 | else: 283 | if coord_type == consts.COORD_FRACTIONAL_BITS_MP_LOWPRECISION: 284 | frac_bits = low_precision 285 | else: 286 | frac_bits = consts.COORD_FRACTIONAL_BITS 287 | 288 | if low_precision: 289 | resolution = consts.COORD_RESOLUTION_LOWPRECISION 290 | else: 291 | resolution = consts.COORD_RESOLUTION 292 | 293 | int_val = self.read_uint_bits(bits) 294 | frac_val = self.read_uint_bits(frac_bits) 295 | 296 | value = int_val + (frac_val * resolution) 297 | 298 | return value 299 | 300 | cpdef bytes read_user_data(self, unsigned int bits): 301 | """Read user data. 302 | 303 | :param bits: Number of bits to read 304 | :type bits: unsigned int 305 | :returns: bytes 306 | """ 307 | cdef unsigned int entries = int(ceil(bits / 8.0)) 308 | cdef array.array[unsigned char] ret = array.array('B', [0] * entries) 309 | cdef unsigned int arr_idx = 0 310 | 311 | if bits % 8 == 0: 312 | while bits != 0: 313 | ret[arr_idx] = self.read_uint_bits(8) 314 | bits -= 8 315 | arr_idx += 1 316 | return bytes(ret) 317 | 318 | arr_idx = 0 319 | while bits >= 8: 320 | ret[arr_idx] = self.read_uint_bits(8) 321 | arr_idx += 1 322 | bits -= 8 323 | 324 | if bits > 0: 325 | ret[arr_idx] = self.read_uint_bits(bits) 326 | 327 | return bytes(ret) 328 | -------------------------------------------------------------------------------- /demoparser/bytebuffer.py: -------------------------------------------------------------------------------- 1 | from demoparser.bitbuffer import Bitbuffer 2 | import struct 3 | 4 | from demoparser.structures import CommandHeader 5 | 6 | 7 | class Bytebuffer: 8 | r"""Parse a stream of bytes from a .DEM file. 9 | 10 | This class provdes convenience methods for parsing 11 | .DEM files. It handles unpacking bytes to different 12 | data types, reading variable-length integers, reading 13 | strings, and creating Bitbuffers. 14 | 15 | :param data: Buffer data 16 | :type data: bytes 17 | 18 | :Example: 19 | 20 | >>> b = Bytebuffer(b'\x00\xf1\xaa') 21 | >>> b.read(1) 22 | 0 23 | >>> b.read_short() 24 | 43761 25 | """ 26 | 27 | def __init__(self, data): 28 | self.data = data 29 | self.index = 0 30 | 31 | def read(self, num_bytes): 32 | """Read `num_bytes` bytes from buffer.""" 33 | d = self.data[self.index:self.index + num_bytes] 34 | self.index += num_bytes 35 | return d 36 | 37 | def read_command_header(self): 38 | """Read the 6 byte command header. 39 | 40 | See :ref:`CommandHeader description ` 41 | for more details. 42 | 43 | :returns: CommandHeader instance 44 | """ 45 | return CommandHeader.from_data(self.read(6)) 46 | 47 | def read_command_data(self): 48 | """Read command info structure. 49 | 50 | This is not used by the parser. 51 | 52 | :returns: bytes 53 | """ 54 | # This data fits the structure described in 55 | # structures.py:CommandInfo. This data seems to always 56 | # be all 0s though. It doesn't appear to be very useful 57 | # and it is very expensive to create one of these structures 58 | # for each message. 59 | self.read(152) 60 | 61 | def read_sequence_data(self): 62 | """Read two integers. 63 | 64 | This data is not used by the parser. 65 | 66 | :returns: Tuple of integers 67 | """ 68 | return struct.unpack("> consts.MAX_EDICT_BITS): 97 | return 98 | 99 | return ent 100 | 101 | def get_one(self, table): 102 | if table in self._cache: 103 | return self._cache[table] 104 | 105 | for e in self._entities: 106 | if e and table in e.props: 107 | self._cache[table] = e 108 | return e 109 | -------------------------------------------------------------------------------- /demoparser/entities/game_rules.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | from demoparser.entities import BaseEntity 3 | 4 | 5 | class GameRules(BaseEntity): 6 | 7 | def __init__(self, parser, index, class_id, serial, props): 8 | self.parser = parser 9 | self.index = index 10 | self.class_id = class_id 11 | self.serial = serial 12 | self.props = props 13 | 14 | def properties(self): 15 | result = { 16 | 'index': self.index, 17 | 'class_id': self.class_id, 18 | 'serial': self.serial, 19 | } 20 | 21 | for name, value in inspect.getmembers(self): 22 | prop_attr = getattr(self.__class__, name, None) 23 | if inspect.isdatadescriptor(prop_attr): 24 | attr = getattr(self, name, None) 25 | if not isinstance(attr, BaseEntity): 26 | result[name] = value 27 | 28 | return result 29 | -------------------------------------------------------------------------------- /demoparser/entities/player.py: -------------------------------------------------------------------------------- 1 | from demoparser.entities import BaseEntity 2 | import inspect 3 | 4 | 5 | class Player(BaseEntity): 6 | 7 | def __init__(self, parser, index, class_id, serial, props): 8 | self.parser = parser 9 | self.index = index 10 | self.slot = self.index - 1 11 | self.class_id = class_id 12 | self.serial = serial 13 | self.props = props 14 | self.user_info = self._get_user_info() 15 | self._serialize_props = [ 16 | 17 | ] 18 | 19 | def _get_user_info(self): 20 | users = self.parser.table_by_name('userinfo')['entries'] 21 | return users[self.slot]['user_data'] 22 | 23 | @property 24 | def health(self): 25 | """Get current health.""" 26 | return self.get_prop('DT_BasePlayer', 'm_iHealth') 27 | 28 | @property 29 | def name(self): 30 | """Get player's name.""" 31 | if isinstance(self.user_info, bytes): 32 | return "" 33 | return self.user_info.name.decode('utf-8') 34 | 35 | @property 36 | def steam_id(self): 37 | """Get Steam ID.""" 38 | return self.user_info.guid.decode('utf-8') 39 | 40 | @property 41 | def position(self): 42 | """Get current position. 43 | 44 | :returns: Position vector. 45 | """ 46 | vec = self.get_prop('DT_CSLocalPlayerExclusive', 'm_vecOrigin') 47 | z = self.get_prop('DT_CSLocalPlayerExclusive', 'm_vecOrigin[2]') 48 | 49 | return { 50 | 'x': vec['x'], 51 | 'y': vec['y'], 52 | 'z': z 53 | } 54 | 55 | @property 56 | def view_angle(self): 57 | """Get current view angle. 58 | 59 | :returns: Tuple of pitch and yaw. 60 | """ 61 | pitch = self.get_prop('DT_CSPlayer', 'm_angEyeAngles[0]') 62 | yaw = self.get_prop('DT_CSPlayer', 'm_angEyeAngles[1]') 63 | 64 | return {'pitch': pitch, 'yaw': yaw} 65 | 66 | @property 67 | def cash(self): 68 | """Get Cash 69 | 70 | :returns: available cash to spend 71 | """ 72 | return self.get_prop('DT_CSPlayer', 'm_iAccount') 73 | 74 | @property 75 | def equipment_value(self): 76 | """Get current equipment value 77 | 78 | :returns: current equipment value 79 | """ 80 | return self.get_prop('DT_CSPlayer', 'm_unCurrentEquipmentValue') 81 | 82 | @property 83 | def is_in_buy_zone(self): 84 | """Check if player is in buy zone 85 | 86 | :returns: boolean 87 | """ 88 | return self.get_prop('DT_CSPlayer', 'm_bInBuyZone') 89 | 90 | @property 91 | def life_state(self): 92 | """Get life state. 93 | 94 | 0 95 | Alive. 96 | 97 | 1 98 | Dying. Either the death animation is still playing 99 | or the player is falling and waiting to hit the ground. 100 | 101 | 2 102 | Dead. Not moving. 103 | 104 | :returns: Life state. 105 | """ 106 | return self.get_prop('DT_BasePlayer', 'm_lifeState') 107 | 108 | @property 109 | def armor(self): 110 | """Get armor value.""" 111 | return self.get_prop('DT_CSPlayer', 'm_ArmorValue') 112 | 113 | @property 114 | def place(self): 115 | """Get last place player occupied. 116 | 117 | A place refers a named navigation mesh. A navigation mesh 118 | represents the walkable areas of a map. 119 | 120 | :returns: Name of last nav mesh occupied. 121 | """ 122 | return self.get_prop('DT_BasePlayer', 'm_szLastPlaceName') 123 | 124 | @property 125 | def kills(self): 126 | """Number of kills for this player.""" 127 | if isinstance(self.user_info, bytes): 128 | return 0 129 | prop = self.parser.entities.get_one('DT_CSPlayerResource') 130 | # This property has a list of kills for all players 131 | kills = prop.props['m_iKills'] 132 | player_id = str(self.user_info.user_id).zfill(3) 133 | return kills[player_id] 134 | 135 | @property 136 | def weapon(self): 137 | """Get current weapon.""" 138 | return self.parser.entities.get_by_handle( 139 | self.get_prop('DT_BaseCombatCharacter', 'm_hActiveWeapon') 140 | ) 141 | 142 | def properties(self): 143 | result = { 144 | 'index': self.index, 145 | 'class_id': self.class_id, 146 | 'serial': self.serial, 147 | } 148 | 149 | for name, value in inspect.getmembers(self): 150 | prop_attr = getattr(self.__class__, name, None) 151 | if inspect.isdatadescriptor(prop_attr): 152 | attr = getattr(self, name, None) 153 | if not isinstance(attr, BaseEntity): 154 | result[name] = value 155 | 156 | return result 157 | -------------------------------------------------------------------------------- /demoparser/entities/team.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | from demoparser.entities import BaseEntity 3 | 4 | 5 | class Team(BaseEntity): 6 | 7 | def __init__(self, parser, index, class_id, serial, props): 8 | self.parser = parser 9 | self.index = index 10 | self.class_id = class_id 11 | self.serial = serial 12 | self.props = props 13 | 14 | @property 15 | def tid(self): 16 | """Team id.""" 17 | return self.get_prop('DT_Team', 'm_iTeamNum') 18 | 19 | @property 20 | def name(self): 21 | """Team name. Either 'T' or 'CT'.""" 22 | return self.get_prop('DT_Team', 'm_szTeamname') 23 | 24 | @property 25 | def clan(self): 26 | """Clan name.""" 27 | return self.get_prop('DT_Team', 'm_szClanTeamname') 28 | 29 | @property 30 | def score(self): 31 | """Final team score.""" 32 | return self.get_prop('DT_Team', 'm_scoreTotal') 33 | 34 | @property 35 | def score_first_half(self): 36 | """Score for first half of match.""" 37 | return self.get_prop('DT_Team', 'm_scoreFirstHalf') 38 | 39 | @property 40 | def score_second_half(self): 41 | """Score for second half of match.""" 42 | return self.get_prop('DT_Team', 'm_scoreSecondHalf') 43 | 44 | def properties(self): 45 | result = { 46 | 'index': self.index, 47 | 'class_id': self.class_id, 48 | 'serial': self.serial, 49 | } 50 | 51 | for name, value in inspect.getmembers(self): 52 | prop_attr = getattr(self.__class__, name, None) 53 | if inspect.isdatadescriptor(prop_attr): 54 | attr = getattr(self, name, None) 55 | if not isinstance(attr, BaseEntity): 56 | result[name] = value 57 | 58 | return result 59 | -------------------------------------------------------------------------------- /demoparser/entities/weapon.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | import inspect 3 | 4 | from demoparser.entities import BaseEntity 5 | 6 | 7 | class WeaponClass(Enum): 8 | PISTOL = 1 9 | RIFLE = 2 10 | SMG = 3 11 | SNIPER = 4 12 | MACHINE_GUN = 5 13 | SHOTGUN = 6 14 | KNIFE = 7 15 | GRENADE = 8 16 | GEAR = 9 17 | 18 | 19 | # As found at: 20 | # unknowncheats.me/wiki/Counter_Strike_Global_Offensive:Economy_Weapon_IDs 21 | weapon_ids = { 22 | 1: { 23 | 'name': 'Desert Eagle', 24 | 'class': WeaponClass.PISTOL, 25 | 'ent_class': 'weapon_deagle' 26 | }, 27 | 2: { 28 | 'name': 'Dual Berettas', 29 | 'class': WeaponClass.PISTOL, 30 | 'ent_class': 'weapon_elite' 31 | }, 32 | 3: { 33 | 'name': 'Five-SeveN', 34 | 'class': WeaponClass.PISTOL, 35 | 'ent_class': 'weapon_fiveseven' 36 | }, 37 | 4: { 38 | 'name': 'Glock-18', 39 | 'class': WeaponClass.PISTOL, 40 | 'ent_class': 'weapon_glock' 41 | }, 42 | 7: { 43 | 'name': 'AK-47', 44 | 'class': WeaponClass.RIFLE, 45 | 'ent_class': 'weapon_ak47' 46 | }, 47 | 8: { 48 | 'name': 'AUG', 49 | 'class': WeaponClass.RIFLE, 50 | 'ent_class': 'weapon_aug' 51 | }, 52 | 9: { 53 | 'name': 'AWP', 54 | 'class': WeaponClass.SNIPER, 55 | 'ent_class': 'weapon_awp' 56 | }, 57 | 10: { 58 | 'name': 'FAMAS', 59 | 'class': WeaponClass.RIFLE, 60 | 'ent_class': 'weapon_famas' 61 | }, 62 | 11: { 63 | 'name': 'G3SG1', 64 | 'class': WeaponClass.SNIPER, 65 | 'ent_class': 'weapon_g3sg1' 66 | }, 67 | 13: { 68 | 'name': 'Galil AR', 69 | 'class': WeaponClass.RIFLE, 70 | 'ent_class': 'weapon_galilar' 71 | }, 72 | 14: { 73 | 'name': 'M249', 74 | 'class': WeaponClass.MACHINE_GUN, 75 | 'ent_class': 'weapon_m249' 76 | }, 77 | 16: { 78 | 'name': 'M4A4', 79 | 'class': WeaponClass.RIFLE, 80 | 'ent_class': 'weapon_m4a1' 81 | }, 82 | 17: { 83 | 'name': 'MAC-10', 84 | 'class': WeaponClass.SMG, 85 | 'ent_class': 'weapon_mac10' 86 | }, 87 | 19: { 88 | 'name': 'P90', 89 | 'class': WeaponClass.SMG, 90 | 'ent_class': 'weapon_p90' 91 | }, 92 | 24: { 93 | 'name': 'UMP-45', 94 | 'class': WeaponClass.SMG, 95 | 'ent_class': 'weapon_ump45' 96 | }, 97 | 25: { 98 | 'name': 'XM1014', 99 | 'class': WeaponClass.SHOTGUN, 100 | 'ent_class': 'weapon_xm1014' 101 | }, 102 | 26: { 103 | 'name': 'PP-Bizon', 104 | 'class': WeaponClass.SMG, 105 | 'ent_class': 'weapon_bizon' 106 | }, 107 | 27: { 108 | 'name': 'MAG-7', 109 | 'class': WeaponClass.SHOTGUN, 110 | 'ent_class': 'weapon_mag7' 111 | }, 112 | 28: { 113 | 'name': 'Negev', 114 | 'class': WeaponClass.MACHINE_GUN, 115 | 'ent_class': 'weapon_negev' 116 | }, 117 | 29: { 118 | 'name': 'Sawed-off Shotgun', 119 | 'class': WeaponClass.SHOTGUN, 120 | 'ent_class': 'weapon_sawedoff' 121 | }, 122 | 30: { 123 | 'name': 'Tec-9', 124 | 'class': WeaponClass.PISTOL, 125 | 'ent_class': 'weapon_tec9' 126 | }, 127 | 31: { 128 | 'name': 'Zeus x27', 129 | 'class': WeaponClass.GEAR, 130 | 'ent_class': 'weapon_taser' 131 | }, 132 | 32: { 133 | 'name': 'P2000', 134 | 'class': WeaponClass.PISTOL, 135 | 'ent_class': 'weapon_hkp2000' 136 | }, 137 | 33: { 138 | 'name': 'MP7', 139 | 'class': WeaponClass.SMG, 140 | 'ent_class': 'weapon_mp7' 141 | }, 142 | 34: { 143 | 'name': 'MP9', 144 | 'class': WeaponClass.SMG, 145 | 'ent_class': 'weapon_mp9' 146 | }, 147 | 35: { 148 | 'name': 'Nova', 149 | 'class': WeaponClass.SHOTGUN, 150 | 'ent_class': 'weapon_nova' 151 | }, 152 | 36: { 153 | 'name': 'P250', 154 | 'class': WeaponClass.PISTOL, 155 | 'ent_class': 'weapon_p250' 156 | }, 157 | 38: { 158 | 'name': 'SCAR-20', 159 | 'class': WeaponClass.SNIPER, 160 | 'ent_class': 'weapon_scar20' 161 | }, 162 | 39: { 163 | 'name': 'SG 553', 164 | 'class': WeaponClass.RIFLE, 165 | 'ent_class': 'weapon_sg556' 166 | }, 167 | 40: { 168 | 'name': 'SSG 08', 169 | 'class': WeaponClass.SNIPER, 170 | 'ent_class': 'weapon_ssg08' 171 | }, 172 | 42: { 173 | 'name': 'Knife', 174 | 'class': WeaponClass.KNIFE, 175 | 'ent_class': 'weapon_knife' 176 | }, 177 | 43: { 178 | 'name': 'Flashbang', 179 | 'class': WeaponClass.GRENADE, 180 | 'ent_class': 'weapon_flashbang' 181 | }, 182 | 44: { 183 | 'name': 'HE Grenade', 184 | 'class': WeaponClass.GRENADE, 185 | 'ent_class': 'weapon_hegrenade' 186 | }, 187 | 45: { 188 | 'name': 'Smoke Grenade', 189 | 'class': WeaponClass.GRENADE, 190 | 'ent_class': 'weapon_smokegrenade' 191 | }, 192 | 46: { 193 | 'name': 'Molotov', 194 | 'class': WeaponClass.GRENADE, 195 | 'ent_class': 'weapon_molotov' 196 | }, 197 | 47: { 198 | 'name': 'Decoy Grenade', 199 | 'class': WeaponClass.GRENADE, 200 | 'ent_class': 'weapon_decoy' 201 | }, 202 | 48: { 203 | 'name': 'Incendiary Grenade', 204 | 'class': WeaponClass.GRENADE, 205 | 'ent_class': 'weapon_incgrenade' 206 | }, 207 | 49: { 208 | 'name': 'C4 Explosive', 209 | 'class': WeaponClass.GEAR, 210 | 'ent_class': 'weapon_c4' 211 | }, 212 | 59: { 213 | 'name': 'Knife', 214 | 'class': WeaponClass.KNIFE, 215 | 'ent_class': 'weapon_knife_t' 216 | }, 217 | 60: { 218 | 'name': 'M4A1-S', 219 | 'class': WeaponClass.RIFLE, 220 | 'ent_class': 'weapon_m4a1_silencer' 221 | }, 222 | 61: { 223 | 'name': 'USP-S', 224 | 'class': WeaponClass.PISTOL, 225 | 'ent_class': 'weapon_usp_silencer' 226 | }, 227 | 63: { 228 | 'name': 'CZ75 Auto', 229 | 'class': WeaponClass.PISTOL, 230 | 'ent_class': 'weapon_cz75a' 231 | }, 232 | 64: { 233 | 'name': 'R8 Revolver', 234 | 'class': WeaponClass.PISTOL, 235 | 'ent_class': 'weapon_revolver' 236 | }, 237 | 500: { 238 | 'name': 'Bayonet', 239 | 'class': WeaponClass.KNIFE, 240 | 'ent_class': 'weapon_bayonet' 241 | }, 242 | 505: { 243 | 'name': 'Flip Knife', 244 | 'class': WeaponClass.KNIFE, 245 | 'ent_class': 'weapon_knife_flip' 246 | }, 247 | 506: { 248 | 'name': 'Gut Knife', 249 | 'class': WeaponClass.KNIFE, 250 | 'ent_class': 'weapon_knife_gut' 251 | }, 252 | 507: { 253 | 'name': 'Karambit', 254 | 'class': WeaponClass.KNIFE, 255 | 'ent_class': 'weapon_knife_karambit' 256 | }, 257 | 508: { 258 | 'name': 'M9 Bayonet', 259 | 'class': WeaponClass.KNIFE, 260 | 'ent_class': 'weapon_knife_m9_bayonet' 261 | }, 262 | 509: { 263 | 'name': 'Huntsman Knife', 264 | 'class': WeaponClass.KNIFE, 265 | 'ent_class': 'weapon_knife_tactical' 266 | }, 267 | 512: { 268 | 'name': 'Falchion Knife', 269 | 'class': WeaponClass.KNIFE, 270 | 'ent_class': 'weapon_knife_falchion' 271 | }, 272 | 514: { 273 | 'name': 'Bowie Knife', 274 | 'class': WeaponClass.KNIFE, 275 | 'ent_class': 'weapon_knife_survival_bowie' 276 | }, 277 | 515: { 278 | 'name': 'Butterfly Knife', 279 | 'class': WeaponClass.KNIFE, 280 | 'ent_class': 'weapon_knife_butterfly' 281 | }, 282 | 516: { 283 | 'name': 'Shadow Daggers', 284 | 'class': WeaponClass.KNIFE, 285 | 'ent_class': 'weapon_knife_push' 286 | } 287 | } 288 | 289 | 290 | class Weapon(BaseEntity): 291 | 292 | def __init__(self, parser, index, class_id, serial, props): 293 | self.parser = parser 294 | self.index = index 295 | self.class_id = class_id 296 | self.serial = serial 297 | self.props = props 298 | 299 | @property 300 | def item_index(self): 301 | return self.get_prop('DT_ScriptCreatedItem', 'm_iItemDefinitionIndex') 302 | 303 | @property 304 | def name(self): 305 | return weapon_ids.get(self.item_index, {}).get('name') 306 | 307 | @property 308 | def weapon_class(self): 309 | return weapon_ids.get(self.item_index, {}).get('class') 310 | 311 | @property 312 | def entity_class(self): 313 | return weapon_ids.get(self.item_index, {}).get('ent_class') 314 | 315 | @property 316 | def previous_owner(self): 317 | handle = self.get_prop('DT_WeaponCSBase', 'm_hPrevOwner') 318 | return self.parser.entities.get_by_handle(handle) 319 | 320 | def properties(self): 321 | result = { 322 | 'index': self.index, 323 | 'class_id': self.class_id, 324 | 'serial': self.serial, 325 | } 326 | 327 | for name, value in inspect.getmembers(self): 328 | prop_attr = getattr(self.__class__, name, None) 329 | if inspect.isdatadescriptor(prop_attr): 330 | attr = getattr(self, name, None) 331 | if not isinstance(attr, BaseEntity): 332 | result[name] = value 333 | 334 | return result 335 | -------------------------------------------------------------------------------- /demoparser/examples/chat.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from demoparser.demofile import DemoFile 4 | 5 | 6 | color_map = { 7 | "\001": "\x1b[0m", 8 | "\002": "\x1b[0;31m", 9 | "\003": "\x1b[0;34m", 10 | "\004": "\x1b[0;32m", 11 | "\005": "\x1b[1;32m", 12 | "\006": "\x1b[1;33m", 13 | "\007": "\x1b[1;31m" 14 | } 15 | 16 | ansi_colors = [ 17 | '\x1b[0;34m', 18 | '\x1b[0;32m', 19 | '\x1b[0;36m', 20 | '\x1b[0;35m', 21 | '\x1b[0;33m', 22 | '\x1b[0;37m', 23 | '\x1b[1;30m', 24 | '\x1b[1;34m', 25 | '\x1b[1;32m', 26 | '\x1b[1;36m', 27 | '\x1b[1;31m', 28 | '\x1b[1;35m', 29 | '\x1b[1;33m', 30 | '\x1b[1;37m' 31 | ] 32 | 33 | 34 | def print_color(text): 35 | t = text 36 | for k, v in color_map.items(): 37 | t = t.replace(k, v) 38 | 39 | print(t) 40 | 41 | 42 | def server_text(msg): 43 | print_color(msg.text) 44 | 45 | 46 | def game_chat(msg): 47 | params = {} 48 | 49 | if msg.msg_name.endswith('AllDead'): 50 | params['dead'] = '\x1b[0;31m* DEAD *\x1b[0m ' 51 | else: 52 | params['dead'] = '' 53 | 54 | params['name'] = '{}{}\x1b[0m'.format( 55 | ansi_colors[msg.ent_idx % 14], msg.params[0] 56 | ) 57 | params['message'] = msg.params[1] 58 | 59 | fmt = "{dead}{name}: {message}".format(**params) 60 | print(fmt) 61 | 62 | 63 | if __name__ == "__main__": 64 | data = open(sys.argv[1], 'rb').read() 65 | d = DemoFile(data) 66 | d.add_callback('SayText', server_text) 67 | d.add_callback('SayText2', game_chat) 68 | d.parse() 69 | -------------------------------------------------------------------------------- /demoparser/examples/player_death.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from demoparser.demofile import DemoFile 4 | 5 | 6 | def death(event, msg): 7 | for idx, key in enumerate(event['event'].keys): 8 | if key.name == 'attacker': 9 | user_id = msg.keys[idx].val_short 10 | attacker = d.entities.get_by_user_id(user_id) 11 | elif key.name == 'userid': 12 | user_id = msg.keys[idx].val_short 13 | victim = d.entities.get_by_user_id(user_id) 14 | elif key.name == 'weapon': 15 | weapon = msg.keys[idx].val_string 16 | elif key.name == 'headshot': 17 | headshot = msg.keys[idx].val_bool 18 | 19 | if attacker and victim: 20 | print("\n --- Player Death at tick {}---".format(d.current_tick)) 21 | print("{} killed by {} with {}. Headshot? {}.\n" 22 | "Attacker: health = {} position = {}\n" 23 | "Victim: position = {}".format( 24 | victim.name, 25 | attacker.name, 26 | weapon, 27 | 'Yes' if headshot else 'No', 28 | attacker.health, 29 | attacker.position, 30 | victim.position)) 31 | 32 | 33 | if __name__ == "__main__": 34 | data = open(sys.argv[1], 'rb').read() 35 | d = DemoFile(data) 36 | d.add_callback('player_death', death) 37 | d.parse() 38 | -------------------------------------------------------------------------------- /demoparser/fields.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | from suitcase.fields import BaseField 4 | from suitcase.fields import BaseStructField 5 | from suitcase.fields import BaseFixedByteSequence 6 | 7 | 8 | class SLFloat32(BaseStructField): 9 | """Signed Little Endian 32-bit float field.""" 10 | PACK_FORMAT = UNPACK_FORMAT = b"" + "I" * l, length, **kwargs) 25 | self.bytes_required = length * 4 26 | 27 | 28 | class FixedLengthString(BaseField): 29 | """A string of a fixed number of bytes. 30 | 31 | The specified number of bytes are read and then any null 32 | bytes are stripped from the result. 33 | 34 | :param length: Number of bytes to read. 35 | :type length: Integer 36 | """ 37 | 38 | def __init__(self, length, **kwargs): 39 | super().__init__(**kwargs) 40 | self.length = length 41 | 42 | @property 43 | def bytes_required(self): 44 | """Number of bytes to read from stream.""" 45 | return self.length 46 | 47 | def pack(self, stream): 48 | stream.write(self._value.strip(b'\0')) 49 | 50 | def unpack(self, data): 51 | self._value = data.strip(b'\0') 52 | -------------------------------------------------------------------------------- /demoparser/props.pxd: -------------------------------------------------------------------------------- 1 | from demoparser.bitbuffer cimport Bitbuffer 2 | 3 | cdef class Decoder: 4 | cdef Bitbuffer buf 5 | cdef dict fprop 6 | cdef object prop 7 | cdef long flags 8 | 9 | # Methods 10 | cpdef object decode(self) 11 | cpdef long decode_int(self) 12 | cpdef float decode_float(self) 13 | cpdef dict decode_vector(self) 14 | cpdef dict decode_vector_xy(self) 15 | cpdef str decode_string(self) 16 | cpdef list decode_array(self) 17 | cpdef float decode_special_float(self) 18 | 19 | cdef enum PropTypes: 20 | DPT_Int = 0 21 | DPT_Float = 1 22 | DPT_Vector = 2 23 | DPT_VectorXY = 3 24 | DPT_String = 4 25 | DPT_Array = 5 26 | DPT_DataTable = 6 27 | DPT_Int64 = 7 28 | DT_MAX_STRING_BITS = 9 29 | 30 | cdef enum PropFlags: 31 | SPROP_UNSIGNED = (1 << 0) 32 | SPROP_COORD = (1 << 1) 33 | SPROP_NOSCALE = (1 << 2) 34 | SPROP_ROUNDDOWN = (1 << 3) 35 | SPROP_ROUNDUP = (1 << 4) 36 | SPROP_NORMAL = (1 << 5) 37 | SPROP_EXCLUDE = (1 << 6) 38 | SPROP_XYZE = (1 << 7) 39 | SPROP_INSIDEARRAY = (1 << 8) 40 | SPROP_PROXY_ALWAYS_YES = (1 << 9) 41 | SPROP_IS_A_VECTOR_ELEM = (1 << 10) 42 | SPROP_COLLAPSIBLE = (1 << 11) 43 | SPROP_COORD_MP = (1 << 12) 44 | SPROP_COORD_MP_LOWPRECISION = (1 << 13) 45 | SPROP_COORD_MP_INTEGRAL = (1 << 14) 46 | SPROP_CELL_COORD = (1 << 15) 47 | SPROP_CELL_COORD_LOWPRECISION = (1 << 16) 48 | SPROP_CELL_COORD_INTEGRAL = (1 << 17) 49 | SPROP_CHANGES_OFTEN = (1 << 18) 50 | SPROP_VARINT = (1 << 19) 51 | -------------------------------------------------------------------------------- /demoparser/props.pyx: -------------------------------------------------------------------------------- 1 | from cython.operator cimport dereference 2 | from demoparser.bitbuffer cimport Bitbuffer 3 | from demoparser import consts 4 | 5 | from libc.math cimport ceil, log2, sqrt, NAN, isnan 6 | cimport cython 7 | 8 | 9 | cdef class Decoder: 10 | """Decode a property. 11 | 12 | Properties of server classes are encoded differently depending 13 | on the type of the property. 14 | 15 | :param buf: Raw buffer data used for decoding 16 | :type buf: Bitbuffer 17 | :param prop: Property to decode 18 | :type prop: object 19 | """ 20 | 21 | def __cinit__(self, Bitbuffer buf, object prop): 22 | self.buf = buf 23 | self.fprop = prop 24 | self.prop = prop['prop'] 25 | self.flags = self.prop.flags 26 | 27 | cpdef object decode(self): 28 | assert self.prop.type != PropTypes.DPT_DataTable 29 | cdef unsigned char prop_type = self.prop.type 30 | 31 | if prop_type == PropTypes.DPT_Int: 32 | ret = self.decode_int() 33 | elif prop_type == PropTypes.DPT_Float: 34 | ret = self.decode_float() 35 | elif prop_type == PropTypes.DPT_Vector: 36 | ret = self.decode_vector() 37 | elif prop_type == PropTypes.DPT_VectorXY: 38 | ret = self.decode_vector_xy() 39 | elif prop_type == PropTypes.DPT_String: 40 | ret = self.decode_string() 41 | elif prop_type == PropTypes.DPT_Int64: 42 | ret = self.decode_int64() 43 | elif prop_type == PropTypes.DPT_Array: 44 | ret = self.decode_array() 45 | else: 46 | raise Exception("Unsupported prop type") 47 | 48 | return ret 49 | 50 | cpdef long decode_int(self): 51 | """Decode an integer. 52 | 53 | Reads the number of bits specified in the 54 | num_bits field of the property. If the property 55 | has the SPROP_UNSIGNED flag set the number will be 56 | read as an unsigned int. 57 | """ 58 | cdef long ret 59 | cdef int num_bits = self.prop.num_bits 60 | 61 | if self.flags & PropFlags.SPROP_UNSIGNED != 0: 62 | if num_bits == 1: 63 | ret = self.buf.read_bit() 64 | else: 65 | ret = self.buf.read_uint_bits(num_bits) 66 | else: 67 | ret = self.buf.read_sint_bits(num_bits) 68 | return ret 69 | 70 | @cython.cdivision(True) 71 | cpdef float decode_float(self): 72 | """Decode a floating point value. 73 | 74 | First the value is decoded as a special float. If that 75 | returns NaN then a normal floating point value is calculated. 76 | """ 77 | cdef float special = self.decode_special_float() 78 | if not isnan(special): 79 | return special 80 | 81 | cdef int interp 82 | cdef float val 83 | cdef int num_bits = self.prop.num_bits 84 | cdef float high_value = self.prop.high_value 85 | cdef float low_value = self.prop.low_value 86 | 87 | interp = self.buf.read_uint_bits(num_bits) 88 | val = interp / ((1 << num_bits) - 1) 89 | val = low_value + (high_value - low_value) * val 90 | 91 | return val 92 | 93 | cpdef dict decode_vector(self): 94 | """Decode a vector. 95 | 96 | :returns: vector 97 | """ 98 | cdef bint sign 99 | cdef float sum_sqr, x, y, z 100 | 101 | x = self.decode_float() 102 | y = self.decode_float() 103 | 104 | if (self.flags & PropFlags.SPROP_NORMAL) == 0: 105 | z = self.decode_float() 106 | else: 107 | sign = self.buf.read_bit() 108 | sum_sqr = (x * x) + (y * y) 109 | if sum_sqr < 1.0: 110 | z = sqrt(1.0 - sum_sqr) 111 | else: 112 | z = 0.0 113 | 114 | if sign: 115 | z = -z 116 | 117 | return { 118 | 'x': x, 119 | 'y': y, 120 | 'z': z 121 | } 122 | 123 | cpdef dict decode_vector_xy(self): 124 | """Decode a two-element vector. 125 | 126 | This only reads the X and Y coordinates for 127 | the vector and sets the Z coordinate to 0.0 128 | 129 | :returns: vector 130 | """ 131 | cdef float x, y 132 | x = self.decode_float() 133 | y = self.decode_float() 134 | return { 135 | 'x': x, 136 | 'y': y, 137 | 'z': 0.0 138 | } 139 | 140 | cpdef str decode_string(self): 141 | r"""Decode a string. 142 | 143 | A fixed number of bits are read which may be 144 | more than the string's length (i.e. some bits 145 | are read after a \0 is encountered). 146 | 147 | :returns: str 148 | """ 149 | cdef unsigned int length = self.buf.read_uint_bits(9) 150 | if not length: 151 | return "" 152 | string = self.buf.read_string(length) 153 | return string 154 | 155 | def decode_int64(self): 156 | """Decode a 64-bit integer. 157 | 158 | CS:GO demos don't appear to contain 64-bit ints. 159 | """ 160 | assert False, 'int64' 161 | 162 | cpdef list decode_array(self): 163 | """Decode array. 164 | 165 | Arrays contain one or more elements (including arrays) which 166 | are recursively decoded. 167 | """ 168 | cdef unsigned int bits, idx, num_elements 169 | 170 | max_elements = self.prop.num_elements 171 | bits = (ceil(log2(max_elements))) + 1 172 | num_elements = self.buf.read_uint_bits(bits) 173 | 174 | elements = [] 175 | for idx in range(num_elements): 176 | prop = {'prop': self.fprop['array_element_prop']} 177 | val = Decoder(self.buf, prop).decode() 178 | elements.append(val) 179 | 180 | return elements 181 | 182 | cpdef float decode_special_float(self): 183 | """Decode a float 184 | 185 | A special float is a float which is interpreted in 186 | some special way. The treatement is determined by 187 | the property's flags. 188 | 189 | +-------------------------+--------------------------------------+ 190 | | Flag | Explanation | 191 | +=========================+======================================+ 192 | | COORD | Treat the float or vector as a world | 193 | | | coordinate. | 194 | +-------------------------+--------------------------------------+ 195 | | COORD_MP | Like COORD but special handling for | 196 | | | multi-player games. | 197 | +-------------------------+--------------------------------------+ 198 | | COORD_MP_LOWPRECISION | Like COORD_MP but the fractional | 199 | | | component uses 3 bits instead of 5. | 200 | +-------------------------+--------------------------------------+ 201 | | COORD_MP_INTEGRAL | Like COORD_MP but coordinates are | 202 | | | rounded to integral boundaries. | 203 | +-------------------------+--------------------------------------+ 204 | | NOSCALE | Don't scale floating-point value to | 205 | | | a range. | 206 | +-------------------------+--------------------------------------+ 207 | | NORMAL | Treat vector as a normal. | 208 | +-------------------------+--------------------------------------+ 209 | | CELL_COORD | Like COORD but has special encoding | 210 | | | for cell coordinates which can't be | 211 | | | negative. | 212 | +-------------------------+--------------------------------------+ 213 | | CELL_COORD_LOWPRECISION | Like CELL_COORD but fractional part | 214 | | | uses 3 bits instead of 5. | 215 | +-------------------------+--------------------------------------+ 216 | | CELL_COORD_INTEGRAL | Like CELL_COORD but coordinates are | 217 | | | rounded to integral boundaries. | 218 | +-------------------------+--------------------------------------+ 219 | """ 220 | cdef float val = NAN 221 | cdef unsigned int f 222 | cdef unsigned int flags = self.flags 223 | 224 | if flags & PropFlags.SPROP_COORD: 225 | val = self.buf.read_bit_coord() 226 | elif flags & PropFlags.SPROP_COORD_MP: 227 | val = self.buf.read_bit_coord_mp(consts.CW_None) 228 | elif flags & PropFlags.SPROP_COORD_MP_LOWPRECISION: 229 | val = self.buf.read_bit_coord_mp(consts.CW_LowPrecision) 230 | elif flags & PropFlags.SPROP_COORD_MP_INTEGRAL: 231 | val = self.buf.read_bit_coord_mp(consts.CW_Integral) 232 | elif flags & PropFlags.SPROP_NOSCALE: 233 | f = self.buf.read_uint_bits(32) 234 | val = dereference(&f) 235 | elif flags & PropFlags.SPROP_NORMAL: 236 | val = self.buf.read_bit_normal() 237 | elif flags & PropFlags.SPROP_CELL_COORD: 238 | val = self.buf.read_bit_cell_coord( 239 | self.prop.num_bits, consts.CW_None 240 | ) 241 | elif flags & PropFlags.SPROP_CELL_COORD_LOWPRECISION: 242 | val = self.buf.read_bit_cell_coord( 243 | self.prop.num_bits, consts.CW_LowPrecision 244 | ) 245 | elif flags & PropFlags.SPROP_CELL_COORD_INTEGRAL: 246 | val = self.buf.read_bit_cell_coord( 247 | self.prop.num_bits, consts.CW_Integral 248 | ) 249 | 250 | return val 251 | -------------------------------------------------------------------------------- /demoparser/protobufs/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Valve Software 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /demoparser/protobufs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibm-dev-incubator/demoparser/5e04604f421a0378b8408e32b5ce36adc7776b37/demoparser/protobufs/__init__.py -------------------------------------------------------------------------------- /demoparser/protobufs/base_gcmessages.proto: -------------------------------------------------------------------------------- 1 | import "demoparser/protobufs/steammessages.proto"; 2 | 3 | option optimize_for = SPEED; 4 | option cc_generic_services = false; 5 | 6 | enum EGCBaseMsg { 7 | k_EMsgGCSystemMessage = 4001; 8 | k_EMsgGCReplicateConVars = 4002; 9 | k_EMsgGCConVarUpdated = 4003; 10 | k_EMsgGCInQueue = 4008; 11 | k_EMsgGCInviteToParty = 4501; 12 | k_EMsgGCInvitationCreated = 4502; 13 | k_EMsgGCPartyInviteResponse = 4503; 14 | k_EMsgGCKickFromParty = 4504; 15 | k_EMsgGCLeaveParty = 4505; 16 | k_EMsgGCServerAvailable = 4506; 17 | k_EMsgGCClientConnectToServer = 4507; 18 | k_EMsgGCGameServerInfo = 4508; 19 | k_EMsgGCError = 4509; 20 | k_EMsgGCReplay_UploadedToYouTube = 4510; 21 | k_EMsgGCLANServerAvailable = 4511; 22 | } 23 | 24 | enum EGCBaseProtoObjectTypes { 25 | k_EProtoObjectPartyInvite = 1001; 26 | k_EProtoObjectLobbyInvite = 1002; 27 | } 28 | 29 | enum GC_BannedWordType { 30 | GC_BANNED_WORD_DISABLE_WORD = 0; 31 | GC_BANNED_WORD_ENABLE_WORD = 1; 32 | } 33 | 34 | message CGCStorePurchaseInit_LineItem { 35 | optional uint32 item_def_id = 1; 36 | optional uint32 quantity = 2; 37 | optional uint32 cost_in_local_currency = 3; 38 | optional uint32 purchase_type = 4; 39 | } 40 | 41 | message CMsgGCStorePurchaseInit { 42 | optional string country = 1; 43 | optional int32 language = 2; 44 | optional int32 currency = 3; 45 | repeated .CGCStorePurchaseInit_LineItem line_items = 4; 46 | } 47 | 48 | message CMsgGCStorePurchaseInitResponse { 49 | optional int32 result = 1; 50 | optional uint64 txn_id = 2; 51 | optional string url = 3; 52 | repeated uint64 item_ids = 4; 53 | } 54 | 55 | message CSOPartyInvite { 56 | optional uint64 group_id = 1 [(key_field) = true]; 57 | optional fixed64 sender_id = 2; 58 | optional string sender_name = 3; 59 | } 60 | 61 | message CSOLobbyInvite { 62 | optional uint64 group_id = 1 [(key_field) = true]; 63 | optional fixed64 sender_id = 2; 64 | optional string sender_name = 3; 65 | } 66 | 67 | message CMsgSystemBroadcast { 68 | optional string message = 1; 69 | } 70 | 71 | message CMsgInviteToParty { 72 | optional fixed64 steam_id = 1; 73 | optional uint32 client_version = 2; 74 | optional uint32 team_invite = 3; 75 | } 76 | 77 | message CMsgInvitationCreated { 78 | optional uint64 group_id = 1; 79 | optional fixed64 steam_id = 2; 80 | } 81 | 82 | message CMsgPartyInviteResponse { 83 | optional uint64 party_id = 1; 84 | optional bool accept = 2; 85 | optional uint32 client_version = 3; 86 | optional uint32 team_invite = 4; 87 | } 88 | 89 | message CMsgKickFromParty { 90 | optional fixed64 steam_id = 1; 91 | } 92 | 93 | message CMsgLeaveParty { 94 | } 95 | 96 | message CMsgServerAvailable { 97 | } 98 | 99 | message CMsgLANServerAvailable { 100 | optional fixed64 lobby_id = 1; 101 | } 102 | 103 | message CSOEconGameAccountClient { 104 | optional uint32 additional_backpack_slots = 1 [default = 0]; 105 | optional fixed32 bonus_xp_timestamp_refresh = 12; 106 | optional uint32 bonus_xp_usedflags = 13; 107 | optional uint32 elevated_state = 14; 108 | optional uint32 elevated_timestamp = 15; 109 | } 110 | 111 | message CSOItemCriteriaCondition { 112 | optional int32 op = 1; 113 | optional string field = 2; 114 | optional bool required = 3; 115 | optional float float_value = 4; 116 | optional string string_value = 5; 117 | } 118 | 119 | message CSOItemCriteria { 120 | optional uint32 item_level = 1; 121 | optional int32 item_quality = 2; 122 | optional bool item_level_set = 3; 123 | optional bool item_quality_set = 4; 124 | optional uint32 initial_inventory = 5; 125 | optional uint32 initial_quantity = 6; 126 | optional bool ignore_enabled_flag = 8; 127 | repeated .CSOItemCriteriaCondition conditions = 9; 128 | optional int32 item_rarity = 10; 129 | optional bool item_rarity_set = 11; 130 | optional bool recent_only = 12; 131 | } 132 | 133 | message CSOItemRecipe { 134 | optional uint32 def_index = 1; 135 | optional string name = 2; 136 | optional string n_a = 3; 137 | optional string desc_inputs = 4; 138 | optional string desc_outputs = 5; 139 | optional string di_a = 6; 140 | optional string di_b = 7; 141 | optional string di_c = 8; 142 | optional string do_a = 9; 143 | optional string do_b = 10; 144 | optional string do_c = 11; 145 | optional bool requires_all_same_class = 12; 146 | optional bool requires_all_same_slot = 13; 147 | optional int32 class_usage_for_output = 14; 148 | optional int32 slot_usage_for_output = 15; 149 | optional int32 set_for_output = 16; 150 | repeated .CSOItemCriteria input_items_criteria = 20; 151 | repeated .CSOItemCriteria output_items_criteria = 21; 152 | repeated uint32 input_item_dupe_counts = 22; 153 | } 154 | 155 | message CMsgDevNewItemRequest { 156 | optional fixed64 receiver = 1; 157 | optional .CSOItemCriteria criteria = 2; 158 | } 159 | 160 | message CMsgIncrementKillCountAttribute { 161 | optional fixed32 killer_account_id = 1; 162 | optional fixed32 victim_account_id = 2; 163 | optional uint64 item_id = 3; 164 | optional uint32 event_type = 4; 165 | optional uint32 amount = 5; 166 | } 167 | 168 | message CMsgApplySticker { 169 | optional uint64 sticker_item_id = 1; 170 | optional uint64 item_item_id = 2; 171 | optional uint32 sticker_slot = 3; 172 | optional uint32 baseitem_defidx = 4; 173 | optional float sticker_wear = 5; 174 | } 175 | 176 | message CMsgApplyStatTrakSwap { 177 | optional uint64 tool_item_id = 1; 178 | optional uint64 item_1_item_id = 2; 179 | optional uint64 item_2_item_id = 3; 180 | } 181 | 182 | message CMsgApplyStrangePart { 183 | optional uint64 strange_part_item_id = 1; 184 | optional uint64 item_item_id = 2; 185 | } 186 | 187 | message CMsgApplyPennantUpgrade { 188 | optional uint64 upgrade_item_id = 1; 189 | optional uint64 pennant_item_id = 2; 190 | } 191 | 192 | message CMsgApplyEggEssence { 193 | optional uint64 essence_item_id = 1; 194 | optional uint64 egg_item_id = 2; 195 | } 196 | 197 | message CSOEconItemAttribute { 198 | optional uint32 def_index = 1; 199 | optional uint32 value = 2; 200 | optional bytes value_bytes = 3; 201 | } 202 | 203 | message CSOEconItemEquipped { 204 | optional uint32 new_class = 1; 205 | optional uint32 new_slot = 2; 206 | } 207 | 208 | message CSOEconItem { 209 | optional uint64 id = 1; 210 | optional uint32 account_id = 2; 211 | optional uint32 inventory = 3; 212 | optional uint32 def_index = 4; 213 | optional uint32 quantity = 5; 214 | optional uint32 level = 6; 215 | optional uint32 quality = 7; 216 | optional uint32 flags = 8 [default = 0]; 217 | optional uint32 origin = 9; 218 | optional string custom_name = 10; 219 | optional string custom_desc = 11; 220 | repeated .CSOEconItemAttribute attribute = 12; 221 | optional .CSOEconItem interior_item = 13; 222 | optional bool in_use = 14 [default = false]; 223 | optional uint32 style = 15 [default = 0]; 224 | optional uint64 original_id = 16 [default = 0]; 225 | repeated .CSOEconItemEquipped equipped_state = 18; 226 | optional uint32 rarity = 19; 227 | } 228 | 229 | message CMsgAdjustItemEquippedState { 230 | optional uint64 item_id = 1; 231 | optional uint32 new_class = 2; 232 | optional uint32 new_slot = 3; 233 | optional bool swap = 4; 234 | } 235 | 236 | message CMsgSortItems { 237 | optional uint32 sort_type = 1; 238 | } 239 | 240 | message CSOEconClaimCode { 241 | optional uint32 account_id = 1; 242 | optional uint32 code_type = 2; 243 | optional uint32 time_acquired = 3; 244 | optional string code = 4; 245 | } 246 | 247 | message CMsgStoreGetUserData { 248 | optional fixed32 price_sheet_version = 1; 249 | optional int32 currency = 2; 250 | } 251 | 252 | message CMsgStoreGetUserDataResponse { 253 | optional int32 result = 1; 254 | optional int32 currency_deprecated = 2; 255 | optional string country_deprecated = 3; 256 | optional fixed32 price_sheet_version = 4; 257 | optional bytes price_sheet = 8; 258 | } 259 | 260 | message CMsgUpdateItemSchema { 261 | optional bytes items_game = 1; 262 | optional fixed32 item_schema_version = 2; 263 | optional string items_game_url_DEPRECATED2013 = 3; 264 | optional string items_game_url = 4; 265 | } 266 | 267 | message CMsgGCError { 268 | optional string error_text = 1; 269 | } 270 | 271 | message CMsgRequestInventoryRefresh { 272 | } 273 | 274 | message CMsgConVarValue { 275 | optional string name = 1; 276 | optional string value = 2; 277 | } 278 | 279 | message CMsgReplicateConVars { 280 | repeated .CMsgConVarValue convars = 1; 281 | } 282 | 283 | message CMsgUseItem { 284 | optional uint64 item_id = 1; 285 | optional fixed64 target_steam_id = 2; 286 | repeated uint32 gift__potential_targets = 3; 287 | optional uint32 duel__class_lock = 4; 288 | optional fixed64 initiator_steam_id = 5; 289 | } 290 | 291 | message CMsgReplayUploadedToYouTube { 292 | optional string youtube_url = 1; 293 | optional string youtube_account_name = 2; 294 | optional uint64 session_id = 3; 295 | } 296 | 297 | message CMsgConsumableExhausted { 298 | optional int32 item_def_id = 1; 299 | } 300 | 301 | message CMsgItemAcknowledged__DEPRECATED { 302 | optional uint32 account_id = 1; 303 | optional uint32 inventory = 2; 304 | optional uint32 def_index = 3; 305 | optional uint32 quality = 4; 306 | optional uint32 rarity = 5; 307 | optional uint32 origin = 6; 308 | optional uint64 item_id = 7; 309 | } 310 | 311 | message CMsgSetItemPositions { 312 | message ItemPosition { 313 | optional uint32 legacy_item_id = 1; 314 | optional uint32 position = 2; 315 | optional uint64 item_id = 3; 316 | } 317 | 318 | repeated .CMsgSetItemPositions.ItemPosition item_positions = 1; 319 | } 320 | 321 | message CMsgGCReportAbuse { 322 | optional fixed64 target_steam_id = 1; 323 | optional string description = 4; 324 | optional uint64 gid = 5; 325 | optional uint32 abuse_type = 2; 326 | optional uint32 content_type = 3; 327 | optional fixed32 target_game_server_ip = 6; 328 | optional uint32 target_game_server_port = 7; 329 | } 330 | 331 | message CMsgGCReportAbuseResponse { 332 | optional fixed64 target_steam_id = 1; 333 | optional uint32 result = 2; 334 | optional string error_message = 3; 335 | } 336 | 337 | message CMsgGCNameItemNotification { 338 | optional fixed64 player_steamid = 1; 339 | optional uint32 item_def_index = 2; 340 | optional string item_name_custom = 3; 341 | } 342 | 343 | message CMsgGCClientDisplayNotification { 344 | optional string notification_title_localization_key = 1; 345 | optional string notification_body_localization_key = 2; 346 | repeated string body_substring_keys = 3; 347 | repeated string body_substring_values = 4; 348 | } 349 | 350 | message CMsgGCShowItemsPickedUp { 351 | optional fixed64 player_steamid = 1; 352 | } 353 | 354 | message CMsgGCIncrementKillCountResponse { 355 | optional uint32 killer_account_id = 1 [(key_field) = true]; 356 | optional uint32 num_kills = 2; 357 | optional uint32 item_def = 3; 358 | optional uint32 level_type = 4; 359 | } 360 | 361 | message CSOEconItemDropRateBonus { 362 | optional uint32 account_id = 1; 363 | optional fixed32 expiration_date = 2; 364 | optional float bonus = 3; 365 | optional uint32 bonus_count = 4; 366 | optional uint64 item_id = 5; 367 | optional uint32 def_index = 6; 368 | } 369 | 370 | message CSOEconItemLeagueViewPass { 371 | optional uint32 account_id = 1 [(key_field) = true]; 372 | optional uint32 league_id = 2 [(key_field) = true]; 373 | optional uint32 admin = 3; 374 | optional uint32 itemindex = 4; 375 | } 376 | 377 | message CSOEconItemEventTicket { 378 | optional uint32 account_id = 1; 379 | optional uint32 event_id = 2; 380 | optional uint64 item_id = 3; 381 | } 382 | 383 | message CMsgGCItemPreviewItemBoughtNotification { 384 | optional uint32 item_def_index = 1; 385 | } 386 | 387 | message CMsgGCStorePurchaseCancel { 388 | optional uint64 txn_id = 1; 389 | } 390 | 391 | message CMsgGCStorePurchaseCancelResponse { 392 | optional uint32 result = 1; 393 | } 394 | 395 | message CMsgGCStorePurchaseFinalize { 396 | optional uint64 txn_id = 1; 397 | } 398 | 399 | message CMsgGCStorePurchaseFinalizeResponse { 400 | optional uint32 result = 1; 401 | repeated uint64 item_ids = 2; 402 | } 403 | 404 | message CMsgGCBannedWordListRequest { 405 | optional uint32 ban_list_group_id = 1; 406 | optional uint32 word_id = 2; 407 | } 408 | 409 | message CMsgGCRequestAnnouncements { 410 | } 411 | 412 | message CMsgGCRequestAnnouncementsResponse { 413 | optional string announcement_title = 1; 414 | optional string announcement = 2; 415 | optional string nextmatch_title = 3; 416 | optional string nextmatch = 4; 417 | } 418 | 419 | message CMsgGCBannedWord { 420 | optional uint32 word_id = 1; 421 | optional .GC_BannedWordType word_type = 2 [default = GC_BANNED_WORD_DISABLE_WORD]; 422 | optional string word = 3; 423 | } 424 | 425 | message CMsgGCBannedWordListResponse { 426 | optional uint32 ban_list_group_id = 1; 427 | repeated .CMsgGCBannedWord word_list = 2; 428 | } 429 | 430 | message CMsgGCToGCBannedWordListBroadcast { 431 | optional .CMsgGCBannedWordListResponse broadcast = 1; 432 | } 433 | 434 | message CMsgGCToGCBannedWordListUpdated { 435 | optional uint32 group_id = 1; 436 | } 437 | 438 | message CSOEconDefaultEquippedDefinitionInstanceClient { 439 | optional uint32 account_id = 1 [(key_field) = true]; 440 | optional uint32 item_definition = 2; 441 | optional uint32 class_id = 3 [(key_field) = true]; 442 | optional uint32 slot_id = 4 [(key_field) = true]; 443 | } 444 | 445 | message CMsgGCToGCDirtySDOCache { 446 | optional uint32 sdo_type = 1; 447 | optional uint64 key_uint64 = 2; 448 | } 449 | 450 | message CMsgGCToGCDirtyMultipleSDOCache { 451 | optional uint32 sdo_type = 1; 452 | repeated uint64 key_uint64 = 2; 453 | } 454 | 455 | message CMsgGCCollectItem { 456 | optional uint64 collection_item_id = 1; 457 | optional uint64 subject_item_id = 2; 458 | } 459 | 460 | message CMsgSDONoMemcached { 461 | } 462 | 463 | message CMsgGCToGCUpdateSQLKeyValue { 464 | optional string key_name = 1; 465 | } 466 | 467 | message CMsgGCToGCIsTrustedServer { 468 | optional fixed64 steam_id = 1; 469 | } 470 | 471 | message CMsgGCToGCIsTrustedServerResponse { 472 | optional bool is_trusted = 1; 473 | } 474 | 475 | message CMsgGCToGCBroadcastConsoleCommand { 476 | optional string con_command = 1; 477 | } 478 | 479 | message CMsgGCServerVersionUpdated { 480 | optional uint32 server_version = 1; 481 | } 482 | 483 | message CMsgGCClientVersionUpdated { 484 | optional uint32 client_version = 1; 485 | } 486 | 487 | message CMsgGCToGCWebAPIAccountChanged { 488 | } 489 | 490 | message CMsgGCToGCRequestPassportItemGrant { 491 | optional fixed64 steam_id = 1; 492 | optional uint32 league_id = 2; 493 | optional int32 reward_flag = 3; 494 | } 495 | 496 | message CMsgGameServerInfo { 497 | enum ServerType { 498 | UNSPECIFIED = 0; 499 | GAME = 1; 500 | PROXY = 2; 501 | } 502 | 503 | optional fixed32 server_public_ip_addr = 1; 504 | optional fixed32 server_private_ip_addr = 2; 505 | optional uint32 server_port = 3; 506 | optional uint32 server_tv_port = 4; 507 | optional string server_key = 5; 508 | optional bool server_hibernation = 6; 509 | optional .CMsgGameServerInfo.ServerType server_type = 7 [default = UNSPECIFIED]; 510 | optional uint32 server_region = 8; 511 | optional float server_loadavg = 9; 512 | optional float server_tv_broadcast_time = 10; 513 | optional float server_game_time = 11; 514 | optional fixed64 server_relay_connected_steam_id = 12; 515 | optional uint32 relay_slots_max = 13; 516 | optional int32 relays_connected = 14; 517 | optional int32 relay_clients_connected = 15; 518 | optional fixed64 relayed_game_server_steam_id = 16; 519 | optional uint32 parent_relay_count = 17; 520 | optional fixed64 tv_secret_code = 18; 521 | } 522 | 523 | -------------------------------------------------------------------------------- /demoparser/protobufs/cstrike15_usermessages.proto: -------------------------------------------------------------------------------- 1 | import "google/protobuf/descriptor.proto"; 2 | import "demoparser/protobufs/netmessages.proto"; 3 | import "demoparser/protobufs/cstrike15_gcmessages.proto"; 4 | 5 | option optimize_for = SPEED; 6 | option cc_generic_services = false; 7 | 8 | enum ECstrike15UserMessages { 9 | CS_UM_VGUIMenu = 1; 10 | CS_UM_Geiger = 2; 11 | CS_UM_Train = 3; 12 | CS_UM_HudText = 4; 13 | CS_UM_SayText = 5; 14 | CS_UM_SayText2 = 6; 15 | CS_UM_TextMsg = 7; 16 | CS_UM_HudMsg = 8; 17 | CS_UM_ResetHud = 9; 18 | CS_UM_GameTitle = 10; 19 | CS_UM_Shake = 12; 20 | CS_UM_Fade = 13; 21 | CS_UM_Rumble = 14; 22 | CS_UM_CloseCaption = 15; 23 | CS_UM_CloseCaptionDirect = 16; 24 | CS_UM_SendAudio = 17; 25 | CS_UM_RawAudio = 18; 26 | CS_UM_VoiceMask = 19; 27 | CS_UM_RequestState = 20; 28 | CS_UM_Damage = 21; 29 | CS_UM_RadioText = 22; 30 | CS_UM_HintText = 23; 31 | CS_UM_KeyHintText = 24; 32 | CS_UM_ProcessSpottedEntityUpdate = 25; 33 | CS_UM_ReloadEffect = 26; 34 | CS_UM_AdjustMoney = 27; 35 | CS_UM_UpdateTeamMoney = 28; 36 | CS_UM_StopSpectatorMode = 29; 37 | CS_UM_KillCam = 30; 38 | CS_UM_DesiredTimescale = 31; 39 | CS_UM_CurrentTimescale = 32; 40 | CS_UM_AchievementEvent = 33; 41 | CS_UM_MatchEndConditions = 34; 42 | CS_UM_DisconnectToLobby = 35; 43 | CS_UM_PlayerStatsUpdate = 36; 44 | CS_UM_DisplayInventory = 37; 45 | CS_UM_WarmupHasEnded = 38; 46 | CS_UM_ClientInfo = 39; 47 | CS_UM_XRankGet = 40; 48 | CS_UM_XRankUpd = 41; 49 | CS_UM_CallVoteFailed = 45; 50 | CS_UM_VoteStart = 46; 51 | CS_UM_VotePass = 47; 52 | CS_UM_VoteFailed = 48; 53 | CS_UM_VoteSetup = 49; 54 | CS_UM_ServerRankRevealAll = 50; 55 | CS_UM_SendLastKillerDamageToClient = 51; 56 | CS_UM_ServerRankUpdate = 52; 57 | CS_UM_ItemPickup = 53; 58 | CS_UM_ShowMenu = 54; 59 | CS_UM_BarTime = 55; 60 | CS_UM_AmmoDenied = 56; 61 | CS_UM_MarkAchievement = 57; 62 | CS_UM_MatchStatsUpdate = 58; 63 | CS_UM_ItemDrop = 59; 64 | CS_UM_GlowPropTurnOff = 60; 65 | CS_UM_SendPlayerItemDrops = 61; 66 | CS_UM_RoundBackupFilenames = 62; 67 | CS_UM_SendPlayerItemFound = 63; 68 | CS_UM_ReportHit = 64; 69 | CS_UM_XpUpdate = 65; 70 | CS_UM_QuestProgress = 66; 71 | CS_UM_ScoreLeaderboardData = 67; 72 | CS_UM_PlayerDecalDigitalSignature = 68; 73 | CS_UM_WeaponSound = 69; 74 | } 75 | 76 | message CCSUsrMsg_VGUIMenu { 77 | message Subkey { 78 | optional string name = 1; 79 | optional string str = 2; 80 | } 81 | 82 | optional string name = 1; 83 | optional bool show = 2; 84 | repeated .CCSUsrMsg_VGUIMenu.Subkey subkeys = 3; 85 | } 86 | 87 | message CCSUsrMsg_Geiger { 88 | optional int32 range = 1; 89 | } 90 | 91 | message CCSUsrMsg_Train { 92 | optional int32 train = 1; 93 | } 94 | 95 | message CCSUsrMsg_HudText { 96 | optional string text = 1; 97 | } 98 | 99 | message CCSUsrMsg_SayText { 100 | optional int32 ent_idx = 1; 101 | optional string text = 2; 102 | optional bool chat = 3; 103 | optional bool textallchat = 4; 104 | } 105 | 106 | message CCSUsrMsg_SayText2 { 107 | optional int32 ent_idx = 1; 108 | optional bool chat = 2; 109 | optional string msg_name = 3; 110 | repeated string params = 4; 111 | optional bool textallchat = 5; 112 | } 113 | 114 | message CCSUsrMsg_TextMsg { 115 | optional int32 msg_dst = 1; 116 | repeated string params = 3; 117 | } 118 | 119 | message CCSUsrMsg_HudMsg { 120 | optional int32 channel = 1; 121 | optional .CMsgVector2D pos = 2; 122 | optional .CMsgRGBA clr1 = 3; 123 | optional .CMsgRGBA clr2 = 4; 124 | optional int32 effect = 5; 125 | optional float fade_in_time = 6; 126 | optional float fade_out_time = 7; 127 | optional float hold_time = 9; 128 | optional float fx_time = 10; 129 | optional string text = 11; 130 | } 131 | 132 | message CCSUsrMsg_Shake { 133 | optional int32 command = 1; 134 | optional float local_amplitude = 2; 135 | optional float frequency = 3; 136 | optional float duration = 4; 137 | } 138 | 139 | message CCSUsrMsg_Fade { 140 | optional int32 duration = 1; 141 | optional int32 hold_time = 2; 142 | optional int32 flags = 3; 143 | optional .CMsgRGBA clr = 4; 144 | } 145 | 146 | message CCSUsrMsg_Rumble { 147 | optional int32 index = 1; 148 | optional int32 data = 2; 149 | optional int32 flags = 3; 150 | } 151 | 152 | message CCSUsrMsg_CloseCaption { 153 | optional uint32 hash = 1; 154 | optional int32 duration = 2; 155 | optional bool from_player = 3; 156 | } 157 | 158 | message CCSUsrMsg_CloseCaptionDirect { 159 | optional uint32 hash = 1; 160 | optional int32 duration = 2; 161 | optional bool from_player = 3; 162 | } 163 | 164 | message CCSUsrMsg_SendAudio { 165 | optional string radio_sound = 1; 166 | } 167 | 168 | message CCSUsrMsg_RawAudio { 169 | optional int32 pitch = 1; 170 | optional int32 entidx = 2; 171 | optional float duration = 3; 172 | optional string voice_filename = 4; 173 | } 174 | 175 | message CCSUsrMsg_VoiceMask { 176 | message PlayerMask { 177 | optional int32 game_rules_mask = 1; 178 | optional int32 ban_masks = 2; 179 | } 180 | 181 | repeated .CCSUsrMsg_VoiceMask.PlayerMask player_masks = 1; 182 | optional bool player_mod_enable = 2; 183 | } 184 | 185 | message CCSUsrMsg_Damage { 186 | optional int32 amount = 1; 187 | optional .CMsgVector inflictor_world_pos = 2; 188 | optional int32 victim_entindex = 3; 189 | } 190 | 191 | message CCSUsrMsg_RadioText { 192 | optional int32 msg_dst = 1; 193 | optional int32 client = 2; 194 | optional string msg_name = 3; 195 | repeated string params = 4; 196 | } 197 | 198 | message CCSUsrMsg_HintText { 199 | optional string text = 1; 200 | } 201 | 202 | message CCSUsrMsg_KeyHintText { 203 | repeated string hints = 1; 204 | } 205 | 206 | message CCSUsrMsg_ProcessSpottedEntityUpdate { 207 | message SpottedEntityUpdate { 208 | optional int32 entity_idx = 1; 209 | optional int32 class_id = 2; 210 | optional int32 origin_x = 3; 211 | optional int32 origin_y = 4; 212 | optional int32 origin_z = 5; 213 | optional int32 angle_y = 6; 214 | optional bool defuser = 7; 215 | optional bool player_has_defuser = 8; 216 | optional bool player_has_c4 = 9; 217 | } 218 | 219 | optional bool new_update = 1; 220 | repeated .CCSUsrMsg_ProcessSpottedEntityUpdate.SpottedEntityUpdate entity_updates = 2; 221 | } 222 | 223 | message CCSUsrMsg_SendPlayerItemDrops { 224 | repeated .CEconItemPreviewDataBlock entity_updates = 1; 225 | } 226 | 227 | message CCSUsrMsg_SendPlayerItemFound { 228 | optional .CEconItemPreviewDataBlock iteminfo = 1; 229 | optional int32 entindex = 2; 230 | } 231 | 232 | message CCSUsrMsg_ReloadEffect { 233 | optional int32 entidx = 1; 234 | optional int32 actanim = 2; 235 | optional float origin_x = 3; 236 | optional float origin_y = 4; 237 | optional float origin_z = 5; 238 | } 239 | 240 | message CCSUsrMsg_WeaponSound { 241 | optional int32 entidx = 1; 242 | optional float origin_x = 2; 243 | optional float origin_y = 3; 244 | optional float origin_z = 4; 245 | optional string sound = 5; 246 | optional float timestamp = 6; 247 | } 248 | 249 | message CCSUsrMsg_AdjustMoney { 250 | optional int32 amount = 1; 251 | } 252 | 253 | message CCSUsrMsg_ReportHit { 254 | optional float pos_x = 1; 255 | optional float pos_y = 2; 256 | optional float timestamp = 4; 257 | optional float pos_z = 3; 258 | } 259 | 260 | message CCSUsrMsg_KillCam { 261 | optional int32 obs_mode = 1; 262 | optional int32 first_target = 2; 263 | optional int32 second_target = 3; 264 | } 265 | 266 | message CCSUsrMsg_DesiredTimescale { 267 | optional float desired_timescale = 1; 268 | optional float duration_realtime_sec = 2; 269 | optional int32 interpolator_type = 3; 270 | optional float start_blend_time = 4; 271 | } 272 | 273 | message CCSUsrMsg_CurrentTimescale { 274 | optional float cur_timescale = 1; 275 | } 276 | 277 | message CCSUsrMsg_AchievementEvent { 278 | optional int32 achievement = 1; 279 | optional int32 count = 2; 280 | optional int32 user_id = 3; 281 | } 282 | 283 | message CCSUsrMsg_MatchEndConditions { 284 | optional int32 fraglimit = 1; 285 | optional int32 mp_maxrounds = 2; 286 | optional int32 mp_winlimit = 3; 287 | optional int32 mp_timelimit = 4; 288 | } 289 | 290 | message CCSUsrMsg_PlayerStatsUpdate { 291 | message Stat { 292 | optional int32 idx = 1; 293 | optional int32 delta = 2; 294 | } 295 | 296 | optional int32 version = 1; 297 | repeated .CCSUsrMsg_PlayerStatsUpdate.Stat stats = 4; 298 | optional int32 user_id = 5; 299 | optional int32 crc = 6; 300 | } 301 | 302 | message CCSUsrMsg_DisplayInventory { 303 | optional bool display = 1; 304 | optional int32 user_id = 2; 305 | } 306 | 307 | message CCSUsrMsg_QuestProgress { 308 | optional uint32 quest_id = 1; 309 | optional uint32 normal_points = 2; 310 | optional uint32 bonus_points = 3; 311 | optional bool is_event_quest = 4; 312 | } 313 | 314 | message CCSUsrMsg_ScoreLeaderboardData { 315 | optional .ScoreLeaderboardData data = 1; 316 | } 317 | 318 | message CCSUsrMsg_PlayerDecalDigitalSignature { 319 | optional .PlayerDecalDigitalSignature data = 1; 320 | } 321 | 322 | message CCSUsrMsg_XRankGet { 323 | optional int32 mode_idx = 1; 324 | optional int32 controller = 2; 325 | } 326 | 327 | message CCSUsrMsg_XRankUpd { 328 | optional int32 mode_idx = 1; 329 | optional int32 controller = 2; 330 | optional int32 ranking = 3; 331 | } 332 | 333 | message CCSUsrMsg_CallVoteFailed { 334 | optional int32 reason = 1; 335 | optional int32 time = 2; 336 | } 337 | 338 | message CCSUsrMsg_VoteStart { 339 | optional int32 team = 1; 340 | optional int32 ent_idx = 2; 341 | optional int32 vote_type = 3; 342 | optional string disp_str = 4; 343 | optional string details_str = 5; 344 | optional string other_team_str = 6; 345 | optional bool is_yes_no_vote = 7; 346 | } 347 | 348 | message CCSUsrMsg_VotePass { 349 | optional int32 team = 1; 350 | optional int32 vote_type = 2; 351 | optional string disp_str = 3; 352 | optional string details_str = 4; 353 | } 354 | 355 | message CCSUsrMsg_VoteFailed { 356 | optional int32 team = 1; 357 | optional int32 reason = 2; 358 | } 359 | 360 | message CCSUsrMsg_VoteSetup { 361 | repeated string potential_issues = 1; 362 | } 363 | 364 | message CCSUsrMsg_SendLastKillerDamageToClient { 365 | optional int32 num_hits_given = 1; 366 | optional int32 damage_given = 2; 367 | optional int32 num_hits_taken = 3; 368 | optional int32 damage_taken = 4; 369 | } 370 | 371 | message CCSUsrMsg_ServerRankUpdate { 372 | message RankUpdate { 373 | optional int32 account_id = 1; 374 | optional int32 rank_old = 2; 375 | optional int32 rank_new = 3; 376 | optional int32 num_wins = 4; 377 | optional float rank_change = 5; 378 | optional int32 rank_type_id = 6; 379 | } 380 | 381 | repeated .CCSUsrMsg_ServerRankUpdate.RankUpdate rank_update = 1; 382 | } 383 | 384 | message CCSUsrMsg_XpUpdate { 385 | optional .CMsgGCCstrike15_v2_GC2ServerNotifyXPRewarded data = 1; 386 | } 387 | 388 | message CCSUsrMsg_ItemPickup { 389 | optional string item = 1; 390 | } 391 | 392 | message CCSUsrMsg_ShowMenu { 393 | optional int32 bits_valid_slots = 1; 394 | optional int32 display_time = 2; 395 | optional string menu_string = 3; 396 | } 397 | 398 | message CCSUsrMsg_BarTime { 399 | optional string time = 1; 400 | } 401 | 402 | message CCSUsrMsg_AmmoDenied { 403 | optional int32 ammoIdx = 1; 404 | } 405 | 406 | message CCSUsrMsg_MarkAchievement { 407 | optional string achievement = 1; 408 | } 409 | 410 | message CCSUsrMsg_MatchStatsUpdate { 411 | optional string update = 1; 412 | } 413 | 414 | message CCSUsrMsg_ItemDrop { 415 | optional int64 itemid = 1; 416 | optional bool death = 2; 417 | } 418 | 419 | message CCSUsrMsg_GlowPropTurnOff { 420 | optional int32 entidx = 1; 421 | } 422 | 423 | message CCSUsrMsg_RoundBackupFilenames { 424 | optional int32 count = 1; 425 | optional int32 index = 2; 426 | optional string filename = 3; 427 | optional string nicename = 4; 428 | } 429 | 430 | message CCSUsrMsg_ResetHud { 431 | optional bool reset = 1; 432 | } 433 | 434 | message CCSUsrMsg_GameTitle { 435 | optional int32 dummy = 1; 436 | } 437 | 438 | message CCSUsrMsg_RequestState { 439 | optional int32 dummy = 1; 440 | } 441 | 442 | message CCSUsrMsg_StopSpectatorMode { 443 | optional int32 dummy = 1; 444 | } 445 | 446 | message CCSUsrMsg_DisconnectToLobby { 447 | optional int32 dummy = 1; 448 | } 449 | 450 | message CCSUsrMsg_WarmupHasEnded { 451 | optional int32 dummy = 1; 452 | } 453 | 454 | message CCSUsrMsg_ClientInfo { 455 | optional int32 dummy = 1; 456 | } 457 | 458 | message CCSUsrMsg_ServerRankRevealAll { 459 | optional int32 seconds_till_shutdown = 1; 460 | } 461 | 462 | -------------------------------------------------------------------------------- /demoparser/protobufs/econ_gcmessages.proto: -------------------------------------------------------------------------------- 1 | import "demoparser/protobufs/steammessages.proto"; 2 | 3 | option optimize_for = SPEED; 4 | option cc_generic_services = false; 5 | 6 | enum EGCItemMsg { 7 | k_EMsgGCBase = 1000; 8 | k_EMsgGCSetItemPosition = 1001; 9 | k_EMsgGCCraft = 1002; 10 | k_EMsgGCCraftResponse = 1003; 11 | k_EMsgGCDelete = 1004; 12 | k_EMsgGCVerifyCacheSubscription = 1005; 13 | k_EMsgGCNameItem = 1006; 14 | k_EMsgGCUnlockCrate = 1007; 15 | k_EMsgGCUnlockCrateResponse = 1008; 16 | k_EMsgGCPaintItem = 1009; 17 | k_EMsgGCPaintItemResponse = 1010; 18 | k_EMsgGCGoldenWrenchBroadcast = 1011; 19 | k_EMsgGCMOTDRequest = 1012; 20 | k_EMsgGCMOTDRequestResponse = 1013; 21 | k_EMsgGCAddItemToSocket_DEPRECATED = 1014; 22 | k_EMsgGCAddItemToSocketResponse_DEPRECATED = 1015; 23 | k_EMsgGCAddSocketToBaseItem_DEPRECATED = 1016; 24 | k_EMsgGCAddSocketToItem_DEPRECATED = 1017; 25 | k_EMsgGCAddSocketToItemResponse_DEPRECATED = 1018; 26 | k_EMsgGCNameBaseItem = 1019; 27 | k_EMsgGCNameBaseItemResponse = 1020; 28 | k_EMsgGCRemoveSocketItem_DEPRECATED = 1021; 29 | k_EMsgGCRemoveSocketItemResponse_DEPRECATED = 1022; 30 | k_EMsgGCCustomizeItemTexture = 1023; 31 | k_EMsgGCCustomizeItemTextureResponse = 1024; 32 | k_EMsgGCUseItemRequest = 1025; 33 | k_EMsgGCUseItemResponse = 1026; 34 | k_EMsgGCGiftedItems_DEPRECATED = 1027; 35 | k_EMsgGCRemoveItemName = 1030; 36 | k_EMsgGCRemoveItemPaint = 1031; 37 | k_EMsgGCGiftWrapItem = 1032; 38 | k_EMsgGCGiftWrapItemResponse = 1033; 39 | k_EMsgGCDeliverGift = 1034; 40 | k_EMsgGCDeliverGiftResponseGiver = 1035; 41 | k_EMsgGCDeliverGiftResponseReceiver = 1036; 42 | k_EMsgGCUnwrapGiftRequest = 1037; 43 | k_EMsgGCUnwrapGiftResponse = 1038; 44 | k_EMsgGCSetItemStyle = 1039; 45 | k_EMsgGCUsedClaimCodeItem = 1040; 46 | k_EMsgGCSortItems = 1041; 47 | k_EMsgGC_RevolvingLootList_DEPRECATED = 1042; 48 | k_EMsgGCLookupAccount = 1043; 49 | k_EMsgGCLookupAccountResponse = 1044; 50 | k_EMsgGCLookupAccountName = 1045; 51 | k_EMsgGCLookupAccountNameResponse = 1046; 52 | k_EMsgGCUpdateItemSchema = 1049; 53 | k_EMsgGCRemoveCustomTexture = 1051; 54 | k_EMsgGCRemoveCustomTextureResponse = 1052; 55 | k_EMsgGCRemoveMakersMark = 1053; 56 | k_EMsgGCRemoveMakersMarkResponse = 1054; 57 | k_EMsgGCRemoveUniqueCraftIndex = 1055; 58 | k_EMsgGCRemoveUniqueCraftIndexResponse = 1056; 59 | k_EMsgGCSaxxyBroadcast = 1057; 60 | k_EMsgGCBackpackSortFinished = 1058; 61 | k_EMsgGCAdjustItemEquippedState = 1059; 62 | k_EMsgGCCollectItem = 1061; 63 | k_EMsgGCItemAcknowledged__DEPRECATED = 1062; 64 | k_EMsgGC_ReportAbuse = 1065; 65 | k_EMsgGC_ReportAbuseResponse = 1066; 66 | k_EMsgGCNameItemNotification = 1068; 67 | k_EMsgGCApplyConsumableEffects = 1069; 68 | k_EMsgGCConsumableExhausted = 1070; 69 | k_EMsgGCShowItemsPickedUp = 1071; 70 | k_EMsgGCClientDisplayNotification = 1072; 71 | k_EMsgGCApplyStrangePart = 1073; 72 | k_EMsgGC_IncrementKillCountAttribute = 1074; 73 | k_EMsgGC_IncrementKillCountResponse = 1075; 74 | k_EMsgGCApplyPennantUpgrade = 1076; 75 | k_EMsgGCSetItemPositions = 1077; 76 | k_EMsgGCApplyEggEssence = 1078; 77 | k_EMsgGCNameEggEssenceResponse = 1079; 78 | k_EMsgGCPaintKitItem = 1080; 79 | k_EMsgGCPaintKitBaseItem = 1081; 80 | k_EMsgGCPaintKitItemResponse = 1082; 81 | k_EMsgGCGiftedItems = 1083; 82 | k_EMsgGCUnlockItemStyle = 1084; 83 | k_EMsgGCUnlockItemStyleResponse = 1085; 84 | k_EMsgGCApplySticker = 1086; 85 | k_EMsgGCItemAcknowledged = 1087; 86 | k_EMsgGCStatTrakSwap = 1088; 87 | k_EMsgGCUserTrackTimePlayedConsecutively = 1089; 88 | k_EMsgGCItemCustomizationNotification = 1090; 89 | k_EMsgGCTradingBase = 1500; 90 | k_EMsgGCTrading_InitiateTradeRequest = 1501; 91 | k_EMsgGCTrading_InitiateTradeResponse = 1502; 92 | k_EMsgGCTrading_StartSession = 1503; 93 | k_EMsgGCTrading_SetItem = 1504; 94 | k_EMsgGCTrading_RemoveItem = 1505; 95 | k_EMsgGCTrading_UpdateTradeInfo = 1506; 96 | k_EMsgGCTrading_SetReadiness = 1507; 97 | k_EMsgGCTrading_ReadinessResponse = 1508; 98 | k_EMsgGCTrading_SessionClosed = 1509; 99 | k_EMsgGCTrading_CancelSession = 1510; 100 | k_EMsgGCTrading_TradeChatMsg = 1511; 101 | k_EMsgGCTrading_ConfirmOffer = 1512; 102 | k_EMsgGCTrading_TradeTypingChatMsg = 1513; 103 | k_EMsgGCServerBrowser_FavoriteServer = 1601; 104 | k_EMsgGCServerBrowser_BlacklistServer = 1602; 105 | k_EMsgGCServerRentalsBase = 1700; 106 | k_EMsgGCItemPreviewCheckStatus = 1701; 107 | k_EMsgGCItemPreviewStatusResponse = 1702; 108 | k_EMsgGCItemPreviewRequest = 1703; 109 | k_EMsgGCItemPreviewRequestResponse = 1704; 110 | k_EMsgGCItemPreviewExpire = 1705; 111 | k_EMsgGCItemPreviewExpireNotification = 1706; 112 | k_EMsgGCItemPreviewItemBoughtNotification = 1707; 113 | k_EMsgGCDev_NewItemRequest = 2001; 114 | k_EMsgGCDev_NewItemRequestResponse = 2002; 115 | k_EMsgGCDev_PaintKitDropItem = 2003; 116 | k_EMsgGCStoreGetUserData = 2500; 117 | k_EMsgGCStoreGetUserDataResponse = 2501; 118 | k_EMsgGCStorePurchaseInit_DEPRECATED = 2502; 119 | k_EMsgGCStorePurchaseInitResponse_DEPRECATED = 2503; 120 | k_EMsgGCStorePurchaseFinalize = 2504; 121 | k_EMsgGCStorePurchaseFinalizeResponse = 2505; 122 | k_EMsgGCStorePurchaseCancel = 2506; 123 | k_EMsgGCStorePurchaseCancelResponse = 2507; 124 | k_EMsgGCStorePurchaseQueryTxn = 2508; 125 | k_EMsgGCStorePurchaseQueryTxnResponse = 2509; 126 | k_EMsgGCStorePurchaseInit = 2510; 127 | k_EMsgGCStorePurchaseInitResponse = 2511; 128 | k_EMsgGCBannedWordListRequest = 2512; 129 | k_EMsgGCBannedWordListResponse = 2513; 130 | k_EMsgGCToGCBannedWordListBroadcast = 2514; 131 | k_EMsgGCToGCBannedWordListUpdated = 2515; 132 | k_EMsgGCToGCDirtySDOCache = 2516; 133 | k_EMsgGCToGCDirtyMultipleSDOCache = 2517; 134 | k_EMsgGCToGCUpdateSQLKeyValue = 2518; 135 | k_EMsgGCToGCIsTrustedServer = 2519; 136 | k_EMsgGCToGCIsTrustedServerResponse = 2520; 137 | k_EMsgGCToGCBroadcastConsoleCommand = 2521; 138 | k_EMsgGCServerVersionUpdated = 2522; 139 | k_EMsgGCApplyAutograph = 2523; 140 | k_EMsgGCToGCWebAPIAccountChanged = 2524; 141 | k_EMsgGCRequestAnnouncements = 2525; 142 | k_EMsgGCRequestAnnouncementsResponse = 2526; 143 | k_EMsgGCRequestPassportItemGrant = 2527; 144 | k_EMsgGCClientVersionUpdated = 2528; 145 | } 146 | 147 | enum EGCMsgResponse { 148 | k_EGCMsgResponseOK = 0; 149 | k_EGCMsgResponseDenied = 1; 150 | k_EGCMsgResponseServerError = 2; 151 | k_EGCMsgResponseTimeout = 3; 152 | k_EGCMsgResponseInvalid = 4; 153 | k_EGCMsgResponseNoMatch = 5; 154 | k_EGCMsgResponseUnknownError = 6; 155 | k_EGCMsgResponseNotLoggedOn = 7; 156 | k_EGCMsgFailedToCreate = 8; 157 | k_EGCMsgLimitExceeded = 9; 158 | k_EGCMsgCommitUnfinalized = 10; 159 | } 160 | 161 | enum EUnlockStyle { 162 | k_UnlockStyle_Succeeded = 0; 163 | k_UnlockStyle_Failed_PreReq = 1; 164 | k_UnlockStyle_Failed_CantAfford = 2; 165 | k_UnlockStyle_Failed_CantCommit = 3; 166 | k_UnlockStyle_Failed_CantLockCache = 4; 167 | k_UnlockStyle_Failed_CantAffordAttrib = 5; 168 | } 169 | 170 | message CMsgGCGiftedItems { 171 | optional uint32 accountid = 1; 172 | optional uint32 giftdefindex = 2; 173 | optional uint32 max_gifts_possible = 3; 174 | optional uint32 num_eligible_recipients = 4; 175 | repeated uint32 recipients_accountids = 5; 176 | } 177 | 178 | message CMsgApplyAutograph { 179 | optional uint64 autograph_item_id = 1; 180 | optional uint64 item_item_id = 2; 181 | } 182 | 183 | message CMsgGCUserTrackTimePlayedConsecutively { 184 | optional uint32 state = 1; 185 | } 186 | 187 | message CMsgGCItemCustomizationNotification { 188 | repeated uint64 item_id = 1; 189 | optional uint32 request = 2; 190 | } 191 | 192 | -------------------------------------------------------------------------------- /demoparser/protobufs/engine_gcmessages.proto: -------------------------------------------------------------------------------- 1 | import "google/protobuf/descriptor.proto"; 2 | 3 | option cc_generic_services = false; 4 | 5 | message CEngineGotvSyncPacket { 6 | optional uint64 match_id = 1; 7 | optional uint32 instance_id = 2; 8 | optional uint32 signupfragment = 3; 9 | optional uint32 currentfragment = 4; 10 | optional float tickrate = 5; 11 | optional uint32 tick = 6; 12 | optional float rtdelay = 8; 13 | optional float rcvage = 9; 14 | optional float keyframe_interval = 10; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /demoparser/protobufs/fatdemo.proto: -------------------------------------------------------------------------------- 1 | import "demoparser/protobufs/netmessages.proto"; 2 | 3 | enum EHitGroup { 4 | EHG_Generic = 0; 5 | EHG_Head = 1; 6 | EHG_Chest = 2; 7 | EHG_Stomach = 3; 8 | EHG_LeftArm = 4; 9 | EHG_RightArm = 5; 10 | EHG_LeftLeg = 6; 11 | EHG_RightLeg = 7; 12 | EHG_Gear = 8; 13 | EHG_Miss = 9; 14 | } 15 | 16 | enum ETeam { 17 | ET_Unknown = 0; 18 | ET_Spectator = 1; 19 | ET_Terrorist = 2; 20 | ET_CT = 3; 21 | } 22 | 23 | enum EWeaponType { 24 | EWT_Knife = 0; 25 | EWT_Pistol = 1; 26 | EWT_SubMachineGun = 2; 27 | EWT_Rifle = 3; 28 | EWT_Shotgun = 4; 29 | EWT_SniperRifle = 5; 30 | EWT_MachineGun = 6; 31 | EWT_C4 = 7; 32 | EWT_Grenade = 8; 33 | EWT_Equipment = 9; 34 | EWT_StackableItem = 10; 35 | EWT_Unknown = 11; 36 | } 37 | 38 | message MLDict { 39 | optional string key = 1; 40 | optional string val_string = 2; 41 | optional int32 val_int = 3; 42 | optional float val_float = 4; 43 | } 44 | 45 | message MLEvent { 46 | optional string event_name = 1; 47 | repeated .MLDict data = 2; 48 | } 49 | 50 | message MLMatchState { 51 | optional string game_mode = 1; 52 | optional string phase = 2; 53 | optional int32 round = 3; 54 | optional int32 score_ct = 4; 55 | optional int32 score_t = 5; 56 | } 57 | 58 | message MLRoundState { 59 | optional string phase = 1; 60 | optional .ETeam win_team = 2 [default = ET_Unknown]; 61 | optional string bomb_state = 3; 62 | } 63 | 64 | message MLWeaponState { 65 | optional int32 index = 1; 66 | optional string name = 2; 67 | optional .EWeaponType type = 3 [default = EWT_Knife]; 68 | optional int32 ammo_clip = 4; 69 | optional int32 ammo_clip_max = 5; 70 | optional int32 ammo_reserve = 6; 71 | optional string state = 7; 72 | optional float recoil_index = 8; 73 | } 74 | 75 | message MLPlayerState { 76 | optional int32 account_id = 1; 77 | optional int32 user_id = 2; 78 | optional int32 entindex = 3; 79 | optional string name = 4; 80 | optional string clan = 5; 81 | optional .ETeam team = 6 [default = ET_Unknown]; 82 | optional .CMsgVector abspos = 7; 83 | optional .CMsgQAngle eyeangle = 8; 84 | optional .CMsgVector eyeangle_fwd = 9; 85 | optional int32 health = 10; 86 | optional int32 armor = 11; 87 | optional float flashed = 12; 88 | optional float smoked = 13; 89 | optional int32 money = 14; 90 | optional int32 round_kills = 15; 91 | optional int32 round_killhs = 16; 92 | optional float burning = 17; 93 | optional bool helmet = 18; 94 | optional bool defuse_kit = 19; 95 | repeated .MLWeaponState weapons = 20; 96 | } 97 | 98 | message MLGameState { 99 | optional .MLMatchState match = 1; 100 | optional .MLRoundState round = 2; 101 | repeated .MLPlayerState players = 3; 102 | } 103 | 104 | message MLDemoHeader { 105 | optional string map_name = 1; 106 | optional int32 tick_rate = 2; 107 | optional uint32 version = 3; 108 | optional uint32 steam_universe = 4; 109 | } 110 | 111 | message MLTick { 112 | optional int32 tick_count = 1; 113 | optional .MLGameState state = 2; 114 | repeated .MLEvent events = 3; 115 | } 116 | 117 | -------------------------------------------------------------------------------- /demoparser/protobufs/gcsdk_gcmessages.proto: -------------------------------------------------------------------------------- 1 | import "demoparser/protobufs/steammessages.proto"; 2 | 3 | option optimize_for = SPEED; 4 | option cc_generic_services = false; 5 | 6 | enum GCClientLauncherType { 7 | GCClientLauncherType_DEFAULT = 0; 8 | GCClientLauncherType_PERFECTWORLD = 1; 9 | } 10 | 11 | enum GCConnectionStatus { 12 | GCConnectionStatus_HAVE_SESSION = 0; 13 | GCConnectionStatus_GC_GOING_DOWN = 1; 14 | GCConnectionStatus_NO_SESSION = 2; 15 | GCConnectionStatus_NO_SESSION_IN_LOGON_QUEUE = 3; 16 | GCConnectionStatus_NO_STEAM = 4; 17 | } 18 | 19 | message CMsgSOIDOwner { 20 | optional uint32 type = 1; 21 | optional uint64 id = 2; 22 | } 23 | 24 | message CMsgSOSingleObject { 25 | optional int32 type_id = 2; 26 | optional bytes object_data = 3; 27 | optional fixed64 version = 4; 28 | optional .CMsgSOIDOwner owner_soid = 5; 29 | } 30 | 31 | message CMsgSOMultipleObjects { 32 | message SingleObject { 33 | option (msgpool_soft_limit) = 256; 34 | option (msgpool_hard_limit) = 1024; 35 | optional int32 type_id = 1; 36 | optional bytes object_data = 2; 37 | } 38 | 39 | repeated .CMsgSOMultipleObjects.SingleObject objects_modified = 2; 40 | optional fixed64 version = 3; 41 | repeated .CMsgSOMultipleObjects.SingleObject objects_added = 4; 42 | repeated .CMsgSOMultipleObjects.SingleObject objects_removed = 5; 43 | optional .CMsgSOIDOwner owner_soid = 6; 44 | } 45 | 46 | message CMsgSOCacheSubscribed { 47 | message SubscribedType { 48 | optional int32 type_id = 1; 49 | repeated bytes object_data = 2; 50 | } 51 | 52 | repeated .CMsgSOCacheSubscribed.SubscribedType objects = 2; 53 | optional fixed64 version = 3; 54 | optional .CMsgSOIDOwner owner_soid = 4; 55 | } 56 | 57 | message CMsgSOCacheUnsubscribed { 58 | optional .CMsgSOIDOwner owner_soid = 2; 59 | } 60 | 61 | message CMsgSOCacheSubscriptionCheck { 62 | optional fixed64 version = 2; 63 | optional .CMsgSOIDOwner owner_soid = 3; 64 | } 65 | 66 | message CMsgSOCacheSubscriptionRefresh { 67 | optional .CMsgSOIDOwner owner_soid = 2; 68 | } 69 | 70 | message CMsgSOCacheVersion { 71 | optional fixed64 version = 1; 72 | } 73 | 74 | message CMsgAccountDetails { 75 | optional bool valid = 1; 76 | optional string account_name = 2; 77 | optional bool public_profile = 4; 78 | optional bool public_inventory = 5; 79 | optional bool vac_banned = 6; 80 | optional bool cyber_cafe = 7; 81 | optional bool school_account = 8; 82 | optional bool free_trial_account = 9; 83 | optional bool subscribed = 10; 84 | optional bool low_violence = 11; 85 | optional bool limited = 12; 86 | optional bool trusted = 13; 87 | optional uint32 package = 14; 88 | optional fixed32 time_cached = 15; 89 | optional bool account_locked = 16; 90 | optional bool community_banned = 17; 91 | optional bool trade_banned = 18; 92 | optional bool eligible_for_community_market = 19; 93 | } 94 | 95 | message CMsgGCMultiplexMessage { 96 | optional uint32 msgtype = 1; 97 | optional bytes payload = 2; 98 | repeated fixed64 steamids = 3; 99 | optional bool replytogc = 4; 100 | } 101 | 102 | message CMsgGCMultiplexMessage_Response { 103 | optional uint32 msgtype = 1; 104 | } 105 | 106 | message CGCToGCMsgMasterAck { 107 | optional uint32 dir_index = 1; 108 | optional uint32 gc_type = 2; 109 | } 110 | 111 | message CGCToGCMsgMasterAck_Response { 112 | optional int32 eresult = 1 [default = 2]; 113 | } 114 | 115 | message CGCToGCMsgMasterStartupComplete { 116 | } 117 | 118 | message CGCToGCMsgRouted { 119 | optional uint32 msg_type = 1; 120 | optional fixed64 sender_id = 2; 121 | optional bytes net_message = 3; 122 | optional uint32 ip = 4; 123 | } 124 | 125 | message CGCToGCMsgRoutedReply { 126 | optional uint32 msg_type = 1; 127 | optional bytes net_message = 2; 128 | } 129 | 130 | message CMsgGCUpdateSessionIP { 131 | optional fixed64 steamid = 1; 132 | optional fixed32 ip = 2; 133 | } 134 | 135 | message CMsgGCRequestSessionIP { 136 | optional fixed64 steamid = 1; 137 | } 138 | 139 | message CMsgGCRequestSessionIPResponse { 140 | optional fixed32 ip = 1; 141 | } 142 | 143 | message CMsgSOCacheHaveVersion { 144 | optional .CMsgSOIDOwner soid = 1; 145 | optional fixed64 version = 2; 146 | } 147 | 148 | message CMsgClientHello { 149 | optional uint32 version = 1; 150 | repeated .CMsgSOCacheHaveVersion socache_have_versions = 2; 151 | optional uint32 client_session_need = 3; 152 | optional uint32 client_launcher = 4; 153 | optional uint32 partner_srcid = 5; 154 | optional uint32 partner_accountid = 6; 155 | optional uint32 partner_accountflags = 7; 156 | optional uint32 partner_accountbalance = 8; 157 | } 158 | 159 | message CMsgServerHello { 160 | optional uint32 version = 1; 161 | repeated .CMsgSOCacheHaveVersion socache_have_versions = 2; 162 | optional uint32 legacy_client_session_need = 3; 163 | optional uint32 client_launcher = 4; 164 | optional uint32 steamdatagram_port = 5; 165 | } 166 | 167 | message CMsgClientWelcome { 168 | message Location { 169 | optional float latitude = 1; 170 | optional float longitude = 2; 171 | optional string country = 3; 172 | } 173 | 174 | optional uint32 version = 1; 175 | optional bytes game_data = 2; 176 | repeated .CMsgSOCacheSubscribed outofdate_subscribed_caches = 3; 177 | repeated .CMsgSOCacheSubscriptionCheck uptodate_subscribed_caches = 4; 178 | optional .CMsgClientWelcome.Location location = 5; 179 | optional bytes game_data2 = 6; 180 | optional uint32 rtime32_gc_welcome_timestamp = 7; 181 | optional uint32 currency = 8; 182 | optional uint32 balance = 9; 183 | optional string balance_url = 10; 184 | optional string txn_country_code = 11; 185 | } 186 | 187 | message CMsgConnectionStatus { 188 | optional .GCConnectionStatus status = 1 [default = GCConnectionStatus_HAVE_SESSION]; 189 | optional uint32 client_session_need = 2; 190 | optional int32 queue_position = 3; 191 | optional int32 queue_size = 4; 192 | optional int32 wait_seconds = 5; 193 | optional int32 estimated_wait_seconds_remaining = 6; 194 | } 195 | 196 | message CWorkshop_PopulateItemDescriptions_Request { 197 | message SingleItemDescription { 198 | optional uint32 gameitemid = 1; 199 | optional string item_description = 2; 200 | optional bool one_per_account = 3; 201 | } 202 | 203 | message ItemDescriptionsLanguageBlock { 204 | optional string language = 1; 205 | repeated .CWorkshop_PopulateItemDescriptions_Request.SingleItemDescription descriptions = 2; 206 | } 207 | 208 | optional uint32 appid = 1; 209 | repeated .CWorkshop_PopulateItemDescriptions_Request.ItemDescriptionsLanguageBlock languages = 2; 210 | } 211 | 212 | message CWorkshop_GetContributors_Request { 213 | optional uint32 appid = 1; 214 | optional uint32 gameitemid = 2; 215 | } 216 | 217 | message CWorkshop_GetContributors_Response { 218 | repeated fixed64 contributors = 1; 219 | } 220 | 221 | message CWorkshop_SetItemPaymentRules_Request { 222 | message WorkshopItemPaymentRule { 223 | optional uint64 workshop_file_id = 1; 224 | optional float revenue_percentage = 2; 225 | optional string rule_description = 3; 226 | } 227 | 228 | message PartnerItemPaymentRule { 229 | optional uint32 account_id = 1; 230 | optional float revenue_percentage = 2; 231 | optional string rule_description = 3; 232 | } 233 | 234 | optional uint32 appid = 1; 235 | optional uint32 gameitemid = 2; 236 | repeated .CWorkshop_SetItemPaymentRules_Request.WorkshopItemPaymentRule associated_workshop_files = 3; 237 | repeated .CWorkshop_SetItemPaymentRules_Request.PartnerItemPaymentRule partner_accounts = 4; 238 | } 239 | 240 | message CWorkshop_SetItemPaymentRules_Response { 241 | } 242 | 243 | message CGameServers_AggregationQuery_Request { 244 | optional string filter = 1; 245 | repeated string group_fields = 3; 246 | } 247 | 248 | message CGameServers_AggregationQuery_Response { 249 | message Group { 250 | repeated string group_values = 1; 251 | optional uint32 servers_empty = 2; 252 | optional uint32 servers_full = 3; 253 | optional uint32 servers_total = 4; 254 | optional uint32 players_humans = 5; 255 | optional uint32 players_bots = 6; 256 | optional uint32 player_capacity = 7; 257 | } 258 | 259 | repeated .CGameServers_AggregationQuery_Response.Group groups = 1; 260 | } 261 | 262 | -------------------------------------------------------------------------------- /demoparser/protobufs/gcsystemmsgs.proto: -------------------------------------------------------------------------------- 1 | option optimize_for = SPEED; 2 | option cc_generic_services = false; 3 | 4 | enum EGCSystemMsg { 5 | k_EGCMsgInvalid = 0; 6 | k_EGCMsgMulti = 1; 7 | k_EGCMsgGenericReply = 10; 8 | k_EGCMsgSystemBase = 50; 9 | k_EGCMsgAchievementAwarded = 51; 10 | k_EGCMsgConCommand = 52; 11 | k_EGCMsgStartPlaying = 53; 12 | k_EGCMsgStopPlaying = 54; 13 | k_EGCMsgStartGameserver = 55; 14 | k_EGCMsgStopGameserver = 56; 15 | k_EGCMsgWGRequest = 57; 16 | k_EGCMsgWGResponse = 58; 17 | k_EGCMsgGetUserGameStatsSchema = 59; 18 | k_EGCMsgGetUserGameStatsSchemaResponse = 60; 19 | k_EGCMsgGetUserStatsDEPRECATED = 61; 20 | k_EGCMsgGetUserStatsResponse = 62; 21 | k_EGCMsgAppInfoUpdated = 63; 22 | k_EGCMsgValidateSession = 64; 23 | k_EGCMsgValidateSessionResponse = 65; 24 | k_EGCMsgLookupAccountFromInput = 66; 25 | k_EGCMsgSendHTTPRequest = 67; 26 | k_EGCMsgSendHTTPRequestResponse = 68; 27 | k_EGCMsgPreTestSetup = 69; 28 | k_EGCMsgRecordSupportAction = 70; 29 | k_EGCMsgGetAccountDetails_DEPRECATED = 71; 30 | k_EGCMsgReceiveInterAppMessage = 73; 31 | k_EGCMsgFindAccounts = 74; 32 | k_EGCMsgPostAlert = 75; 33 | k_EGCMsgGetLicenses = 76; 34 | k_EGCMsgGetUserStats = 77; 35 | k_EGCMsgGetCommands = 78; 36 | k_EGCMsgGetCommandsResponse = 79; 37 | k_EGCMsgAddFreeLicense = 80; 38 | k_EGCMsgAddFreeLicenseResponse = 81; 39 | k_EGCMsgGetIPLocation = 82; 40 | k_EGCMsgGetIPLocationResponse = 83; 41 | k_EGCMsgSystemStatsSchema = 84; 42 | k_EGCMsgGetSystemStats = 85; 43 | k_EGCMsgGetSystemStatsResponse = 86; 44 | k_EGCMsgSendEmail = 87; 45 | k_EGCMsgSendEmailResponse = 88; 46 | k_EGCMsgGetEmailTemplate = 89; 47 | k_EGCMsgGetEmailTemplateResponse = 90; 48 | k_EGCMsgGrantGuestPass = 91; 49 | k_EGCMsgGrantGuestPassResponse = 92; 50 | k_EGCMsgGetAccountDetails = 93; 51 | k_EGCMsgGetAccountDetailsResponse = 94; 52 | k_EGCMsgGetPersonaNames = 95; 53 | k_EGCMsgGetPersonaNamesResponse = 96; 54 | k_EGCMsgMultiplexMsg = 97; 55 | k_EGCMsgMultiplexMsgResponse = 98; 56 | k_EGCMsgWebAPIRegisterInterfaces = 101; 57 | k_EGCMsgWebAPIJobRequest = 102; 58 | k_EGCMsgWebAPIJobRequestHttpResponse = 104; 59 | k_EGCMsgWebAPIJobRequestForwardResponse = 105; 60 | k_EGCMsgMemCachedGet = 200; 61 | k_EGCMsgMemCachedGetResponse = 201; 62 | k_EGCMsgMemCachedSet = 202; 63 | k_EGCMsgMemCachedDelete = 203; 64 | k_EGCMsgMemCachedStats = 204; 65 | k_EGCMsgMemCachedStatsResponse = 205; 66 | k_EGCMsgMasterSetDirectory = 220; 67 | k_EGCMsgMasterSetDirectoryResponse = 221; 68 | k_EGCMsgMasterSetWebAPIRouting = 222; 69 | k_EGCMsgMasterSetWebAPIRoutingResponse = 223; 70 | k_EGCMsgMasterSetClientMsgRouting = 224; 71 | k_EGCMsgMasterSetClientMsgRoutingResponse = 225; 72 | k_EGCMsgSetOptions = 226; 73 | k_EGCMsgSetOptionsResponse = 227; 74 | k_EGCMsgSystemBase2 = 500; 75 | k_EGCMsgGetPurchaseTrustStatus = 501; 76 | k_EGCMsgGetPurchaseTrustStatusResponse = 502; 77 | k_EGCMsgUpdateSession = 503; 78 | k_EGCMsgGCAccountVacStatusChange = 504; 79 | k_EGCMsgCheckFriendship = 505; 80 | k_EGCMsgCheckFriendshipResponse = 506; 81 | k_EGCMsgGetPartnerAccountLink = 507; 82 | k_EGCMsgGetPartnerAccountLinkResponse = 508; 83 | k_EGCMsgDPPartnerMicroTxns = 512; 84 | k_EGCMsgDPPartnerMicroTxnsResponse = 513; 85 | k_EGCMsgVacVerificationChange = 518; 86 | k_EGCMsgAccountPhoneNumberChange = 519; 87 | k_EGCMsgInviteUserToLobby = 523; 88 | k_EGCMsgGetGamePersonalDataCategoriesRequest = 524; 89 | k_EGCMsgGetGamePersonalDataCategoriesResponse = 525; 90 | k_EGCMsgGetGamePersonalDataEntriesRequest = 526; 91 | k_EGCMsgGetGamePersonalDataEntriesResponse = 527; 92 | k_EGCMsgTerminateGamePersonalDataEntriesRequest = 528; 93 | k_EGCMsgTerminateGamePersonalDataEntriesResponse = 529; 94 | } 95 | 96 | enum ESOMsg { 97 | k_ESOMsg_Create = 21; 98 | k_ESOMsg_Update = 22; 99 | k_ESOMsg_Destroy = 23; 100 | k_ESOMsg_CacheSubscribed = 24; 101 | k_ESOMsg_CacheUnsubscribed = 25; 102 | k_ESOMsg_UpdateMultiple = 26; 103 | k_ESOMsg_CacheSubscriptionCheck = 27; 104 | k_ESOMsg_CacheSubscriptionRefresh = 28; 105 | } 106 | 107 | enum EGCBaseClientMsg { 108 | k_EMsgGCClientWelcome = 4004; 109 | k_EMsgGCServerWelcome = 4005; 110 | k_EMsgGCClientHello = 4006; 111 | k_EMsgGCServerHello = 4007; 112 | k_EMsgGCClientConnectionStatus = 4009; 113 | k_EMsgGCServerConnectionStatus = 4010; 114 | k_EMsgGCClientHelloPartner = 4011; 115 | k_EMsgGCClientHelloPW = 4012; 116 | k_EMsgGCClientHelloR2 = 4013; 117 | k_EMsgGCClientHelloR3 = 4014; 118 | k_EMsgGCClientHelloR4 = 4015; 119 | } 120 | 121 | enum EGCToGCMsg { 122 | k_EGCToGCMsgMasterAck = 150; 123 | k_EGCToGCMsgMasterAckResponse = 151; 124 | k_EGCToGCMsgRouted = 152; 125 | k_EGCToGCMsgRoutedReply = 153; 126 | k_EMsgUpdateSessionIP = 154; 127 | k_EMsgRequestSessionIP = 155; 128 | k_EMsgRequestSessionIPResponse = 156; 129 | k_EGCToGCMsgMasterStartupComplete = 157; 130 | } 131 | 132 | message CMsgGCHVacVerificationChange { 133 | optional fixed64 steamid = 1; 134 | optional uint32 appid = 2; 135 | optional bool is_verified = 3; 136 | } 137 | 138 | message CMsgGCHAccountPhoneNumberChange { 139 | optional fixed64 steamid = 1; 140 | optional uint32 appid = 2; 141 | optional uint64 phone_id = 3; 142 | optional bool is_verified = 4; 143 | optional bool is_identifying = 5; 144 | } 145 | 146 | message CMsgGCHInviteUserToLobby { 147 | optional fixed64 steamid = 1; 148 | optional uint32 appid = 2; 149 | optional fixed64 steamid_invited = 3; 150 | optional fixed64 steamid_lobby = 4; 151 | } 152 | 153 | message CCommunity_GamePersonalDataCategoryInfo { 154 | optional string type = 1; 155 | optional string localization_token = 2; 156 | optional string template_file = 3; 157 | } 158 | 159 | message CCommunity_GetGamePersonalDataCategories_Request { 160 | optional uint32 appid = 1; 161 | } 162 | 163 | message CCommunity_GetGamePersonalDataCategories_Response { 164 | repeated .CCommunity_GamePersonalDataCategoryInfo categories = 1; 165 | optional string app_assets_basename = 2; 166 | } 167 | 168 | message CCommunity_GetGamePersonalDataEntries_Request { 169 | optional uint32 appid = 1; 170 | optional uint64 steamid = 2; 171 | optional string type = 3; 172 | optional string continue_token = 4; 173 | } 174 | 175 | message CCommunity_GetGamePersonalDataEntries_Response { 176 | optional uint32 gceresult = 1; 177 | repeated string entries = 2; 178 | optional string continue_token = 3; 179 | } 180 | 181 | message CCommunity_TerminateGamePersonalDataEntries_Request { 182 | optional uint32 appid = 1; 183 | optional uint64 steamid = 2; 184 | } 185 | 186 | message CCommunity_TerminateGamePersonalDataEntries_Response { 187 | optional uint32 gceresult = 1; 188 | } 189 | 190 | -------------------------------------------------------------------------------- /demoparser/protobufs/netmessages.proto: -------------------------------------------------------------------------------- 1 | import "google/protobuf/descriptor.proto"; 2 | 3 | option cc_generic_services = false; 4 | 5 | enum NET_Messages { 6 | net_NOP = 0; 7 | net_Disconnect = 1; 8 | net_File = 2; 9 | net_SplitScreenUser = 3; 10 | net_Tick = 4; 11 | net_StringCmd = 5; 12 | net_SetConVar = 6; 13 | net_SignonState = 7; 14 | net_PlayerAvatarData = 100; 15 | } 16 | 17 | enum CLC_Messages { 18 | clc_ClientInfo = 8; 19 | clc_Move = 9; 20 | clc_VoiceData = 10; 21 | clc_BaselineAck = 11; 22 | clc_ListenEvents = 12; 23 | clc_RespondCvarValue = 13; 24 | clc_FileCRCCheck = 14; 25 | clc_LoadingProgress = 15; 26 | clc_SplitPlayerConnect = 16; 27 | clc_ClientMessage = 17; 28 | clc_CmdKeyValues = 18; 29 | clc_HltvReplay = 20; 30 | } 31 | 32 | enum VoiceDataFormat_t { 33 | VOICEDATA_FORMAT_STEAM = 0; 34 | VOICEDATA_FORMAT_ENGINE = 1; 35 | } 36 | 37 | enum ESplitScreenMessageType { 38 | option allow_alias = true; 39 | MSG_SPLITSCREEN_ADDUSER = 0; 40 | MSG_SPLITSCREEN_REMOVEUSER = 1; 41 | MSG_SPLITSCREEN_TYPE_BITS = 1; 42 | } 43 | 44 | enum SVC_Messages { 45 | svc_ServerInfo = 8; 46 | svc_SendTable = 9; 47 | svc_ClassInfo = 10; 48 | svc_SetPause = 11; 49 | svc_CreateStringTable = 12; 50 | svc_UpdateStringTable = 13; 51 | svc_VoiceInit = 14; 52 | svc_VoiceData = 15; 53 | svc_Print = 16; 54 | svc_Sounds = 17; 55 | svc_SetView = 18; 56 | svc_FixAngle = 19; 57 | svc_CrosshairAngle = 20; 58 | svc_BSPDecal = 21; 59 | svc_SplitScreen = 22; 60 | svc_UserMessage = 23; 61 | svc_EntityMessage = 24; 62 | svc_GameEvent = 25; 63 | svc_PacketEntities = 26; 64 | svc_TempEntities = 27; 65 | svc_Prefetch = 28; 66 | svc_Menu = 29; 67 | svc_GameEventList = 30; 68 | svc_GetCvarValue = 31; 69 | svc_PaintmapData = 33; 70 | svc_CmdKeyValues = 34; 71 | svc_EncryptedData = 35; 72 | svc_HltvReplay = 36; 73 | svc_Broadcast_Command = 38; 74 | } 75 | 76 | enum ReplayEventType_t { 77 | REPLAY_EVENT_CANCEL = 0; 78 | REPLAY_EVENT_DEATH = 1; 79 | REPLAY_EVENT_GENERIC = 2; 80 | REPLAY_EVENT_STUCK_NEED_FULL_UPDATE = 3; 81 | } 82 | 83 | message CMsgVector { 84 | optional float x = 1; 85 | optional float y = 2; 86 | optional float z = 3; 87 | } 88 | 89 | message CMsgVector2D { 90 | optional float x = 1; 91 | optional float y = 2; 92 | } 93 | 94 | message CMsgQAngle { 95 | optional float x = 1; 96 | optional float y = 2; 97 | optional float z = 3; 98 | } 99 | 100 | message CMsgRGBA { 101 | optional int32 r = 1; 102 | optional int32 g = 2; 103 | optional int32 b = 3; 104 | optional int32 a = 4; 105 | } 106 | 107 | message CNETMsg_Tick { 108 | optional uint32 tick = 1; 109 | optional uint32 host_computationtime = 4; 110 | optional uint32 host_computationtime_std_deviation = 5; 111 | optional uint32 host_framestarttime_std_deviation = 6; 112 | optional uint32 hltv_replay_flags = 7; 113 | } 114 | 115 | message CNETMsg_StringCmd { 116 | optional string command = 1; 117 | } 118 | 119 | message CNETMsg_SignonState { 120 | optional uint32 signon_state = 1; 121 | optional uint32 spawn_count = 2; 122 | optional uint32 num_server_players = 3; 123 | repeated string players_networkids = 4; 124 | optional string map_name = 5; 125 | } 126 | 127 | message CMsg_CVars { 128 | message CVar { 129 | optional string name = 1; 130 | optional string value = 2; 131 | optional uint32 dictionary_name = 3; 132 | } 133 | 134 | repeated .CMsg_CVars.CVar cvars = 1; 135 | } 136 | 137 | message CNETMsg_SetConVar { 138 | optional .CMsg_CVars convars = 1; 139 | } 140 | 141 | message CNETMsg_NOP { 142 | } 143 | 144 | message CNETMsg_Disconnect { 145 | optional string text = 1; 146 | } 147 | 148 | message CNETMsg_File { 149 | optional int32 transfer_id = 1; 150 | optional string file_name = 2; 151 | optional bool is_replay_demo_file = 3; 152 | optional bool deny = 4; 153 | } 154 | 155 | message CNETMsg_SplitScreenUser { 156 | optional int32 slot = 1; 157 | } 158 | 159 | message CNETMsg_PlayerAvatarData { 160 | optional uint32 accountid = 1; 161 | optional bytes rgb = 2; 162 | } 163 | 164 | message CCLCMsg_ClientInfo { 165 | optional fixed32 send_table_crc = 1; 166 | optional uint32 server_count = 2; 167 | optional bool is_hltv = 3; 168 | optional bool is_replay = 4; 169 | optional uint32 friends_id = 5; 170 | optional string friends_name = 6; 171 | repeated fixed32 custom_files = 7; 172 | } 173 | 174 | message CCLCMsg_Move { 175 | optional uint32 num_backup_commands = 1; 176 | optional uint32 num_new_commands = 2; 177 | optional bytes data = 3; 178 | } 179 | 180 | message CCLCMsg_VoiceData { 181 | optional bytes data = 1; 182 | optional fixed64 xuid = 2; 183 | optional .VoiceDataFormat_t format = 3 [default = VOICEDATA_FORMAT_ENGINE]; 184 | optional int32 sequence_bytes = 4; 185 | optional uint32 section_number = 5; 186 | optional uint32 uncompressed_sample_offset = 6; 187 | } 188 | 189 | message CCLCMsg_BaselineAck { 190 | optional int32 baseline_tick = 1; 191 | optional int32 baseline_nr = 2; 192 | } 193 | 194 | message CCLCMsg_ListenEvents { 195 | repeated fixed32 event_mask = 1; 196 | } 197 | 198 | message CCLCMsg_RespondCvarValue { 199 | optional int32 cookie = 1; 200 | optional int32 status_code = 2; 201 | optional string name = 3; 202 | optional string value = 4; 203 | } 204 | 205 | message CCLCMsg_FileCRCCheck { 206 | optional int32 code_path = 1; 207 | optional string path = 2; 208 | optional int32 code_filename = 3; 209 | optional string filename = 4; 210 | optional int32 file_fraction = 5; 211 | optional bytes md5 = 6; 212 | optional uint32 crc = 7; 213 | optional int32 file_hash_type = 8; 214 | optional int32 file_len = 9; 215 | optional int32 pack_file_id = 10; 216 | optional int32 pack_file_number = 11; 217 | } 218 | 219 | message CCLCMsg_LoadingProgress { 220 | optional int32 progress = 1; 221 | } 222 | 223 | message CCLCMsg_SplitPlayerConnect { 224 | optional .CMsg_CVars convars = 1; 225 | } 226 | 227 | message CCLCMsg_CmdKeyValues { 228 | optional bytes keyvalues = 1; 229 | } 230 | 231 | message CSVCMsg_ServerInfo { 232 | optional int32 protocol = 1; 233 | optional int32 server_count = 2; 234 | optional bool is_dedicated = 3; 235 | optional bool is_official_valve_server = 4; 236 | optional bool is_hltv = 5; 237 | optional bool is_replay = 6; 238 | optional bool is_redirecting_to_proxy_relay = 21; 239 | optional int32 c_os = 7; 240 | optional fixed32 map_crc = 8; 241 | optional fixed32 client_crc = 9; 242 | optional fixed32 string_table_crc = 10; 243 | optional int32 max_clients = 11; 244 | optional int32 max_classes = 12; 245 | optional int32 player_slot = 13; 246 | optional float tick_interval = 14; 247 | optional string game_dir = 15; 248 | optional string map_name = 16; 249 | optional string map_group_name = 17; 250 | optional string sky_name = 18; 251 | optional string host_name = 19; 252 | optional uint32 public_ip = 20; 253 | optional uint64 ugc_map_id = 22; 254 | } 255 | 256 | message CSVCMsg_ClassInfo { 257 | message class_t { 258 | optional int32 class_id = 1; 259 | optional string data_table_name = 2; 260 | optional string class_name = 3; 261 | } 262 | 263 | optional bool create_on_client = 1; 264 | repeated .CSVCMsg_ClassInfo.class_t classes = 2; 265 | } 266 | 267 | message CSVCMsg_SendTable { 268 | message sendprop_t { 269 | optional int32 type = 1; 270 | optional string var_name = 2; 271 | optional int32 flags = 3; 272 | optional int32 priority = 4; 273 | optional string dt_name = 5; 274 | optional int32 num_elements = 6; 275 | optional float low_value = 7; 276 | optional float high_value = 8; 277 | optional int32 num_bits = 9; 278 | } 279 | 280 | optional bool is_end = 1; 281 | optional string net_table_name = 2; 282 | optional bool needs_decoder = 3; 283 | repeated .CSVCMsg_SendTable.sendprop_t props = 4; 284 | } 285 | 286 | message CSVCMsg_Print { 287 | optional string text = 1; 288 | } 289 | 290 | message CSVCMsg_SetPause { 291 | optional bool paused = 1; 292 | } 293 | 294 | message CSVCMsg_SetView { 295 | optional int32 entity_index = 1; 296 | } 297 | 298 | message CSVCMsg_CreateStringTable { 299 | optional string name = 1; 300 | optional int32 max_entries = 2; 301 | optional int32 num_entries = 3; 302 | optional bool user_data_fixed_size = 4; 303 | optional int32 user_data_size = 5; 304 | optional int32 user_data_size_bits = 6; 305 | optional int32 flags = 7; 306 | optional bytes string_data = 8; 307 | } 308 | 309 | message CSVCMsg_UpdateStringTable { 310 | optional int32 table_id = 1; 311 | optional int32 num_changed_entries = 2; 312 | optional bytes string_data = 3; 313 | } 314 | 315 | message CSVCMsg_VoiceInit { 316 | optional int32 quality = 1; 317 | optional string codec = 2; 318 | optional int32 version = 3 [default = 0]; 319 | } 320 | 321 | message CSVCMsg_VoiceData { 322 | optional int32 client = 1; 323 | optional bool proximity = 2; 324 | optional fixed64 xuid = 3; 325 | optional int32 audible_mask = 4; 326 | optional bytes voice_data = 5; 327 | optional bool caster = 6; 328 | optional .VoiceDataFormat_t format = 7 [default = VOICEDATA_FORMAT_ENGINE]; 329 | optional int32 sequence_bytes = 8; 330 | optional uint32 section_number = 9; 331 | optional uint32 uncompressed_sample_offset = 10; 332 | } 333 | 334 | message CSVCMsg_FixAngle { 335 | optional bool relative = 1; 336 | optional .CMsgQAngle angle = 2; 337 | } 338 | 339 | message CSVCMsg_CrosshairAngle { 340 | optional .CMsgQAngle angle = 1; 341 | } 342 | 343 | message CSVCMsg_Prefetch { 344 | optional int32 sound_index = 1; 345 | } 346 | 347 | message CSVCMsg_BSPDecal { 348 | optional .CMsgVector pos = 1; 349 | optional int32 decal_texture_index = 2; 350 | optional int32 entity_index = 3; 351 | optional int32 model_index = 4; 352 | optional bool low_priority = 5; 353 | } 354 | 355 | message CSVCMsg_SplitScreen { 356 | optional .ESplitScreenMessageType type = 1 [default = MSG_SPLITSCREEN_ADDUSER]; 357 | optional int32 slot = 2; 358 | optional int32 player_index = 3; 359 | } 360 | 361 | message CSVCMsg_GetCvarValue { 362 | optional int32 cookie = 1; 363 | optional string cvar_name = 2; 364 | } 365 | 366 | message CSVCMsg_Menu { 367 | optional int32 dialog_type = 1; 368 | optional bytes menu_key_values = 2; 369 | } 370 | 371 | message CSVCMsg_UserMessage { 372 | optional int32 msg_type = 1; 373 | optional bytes msg_data = 2; 374 | optional int32 passthrough = 3; 375 | } 376 | 377 | message CSVCMsg_PaintmapData { 378 | optional bytes paintmap = 1; 379 | } 380 | 381 | message CSVCMsg_GameEvent { 382 | message key_t { 383 | optional int32 type = 1; 384 | optional string val_string = 2; 385 | optional float val_float = 3; 386 | optional int32 val_long = 4; 387 | optional int32 val_short = 5; 388 | optional int32 val_byte = 6; 389 | optional bool val_bool = 7; 390 | optional uint64 val_uint64 = 8; 391 | optional bytes val_wstring = 9; 392 | } 393 | 394 | optional string event_name = 1; 395 | optional int32 eventid = 2; 396 | repeated .CSVCMsg_GameEvent.key_t keys = 3; 397 | optional int32 passthrough = 4; 398 | } 399 | 400 | message CSVCMsg_GameEventList { 401 | message key_t { 402 | optional int32 type = 1; 403 | optional string name = 2; 404 | } 405 | 406 | message descriptor_t { 407 | optional int32 eventid = 1; 408 | optional string name = 2; 409 | repeated .CSVCMsg_GameEventList.key_t keys = 3; 410 | } 411 | 412 | repeated .CSVCMsg_GameEventList.descriptor_t descriptors = 1; 413 | } 414 | 415 | message CSVCMsg_TempEntities { 416 | optional bool reliable = 1; 417 | optional int32 num_entries = 2; 418 | optional bytes entity_data = 3; 419 | } 420 | 421 | message CSVCMsg_PacketEntities { 422 | optional int32 max_entries = 1; 423 | optional int32 updated_entries = 2; 424 | optional bool is_delta = 3; 425 | optional bool update_baseline = 4; 426 | optional int32 baseline = 5; 427 | optional int32 delta_from = 6; 428 | optional bytes entity_data = 7; 429 | } 430 | 431 | message CSVCMsg_Sounds { 432 | message sounddata_t { 433 | optional sint32 origin_x = 1; 434 | optional sint32 origin_y = 2; 435 | optional sint32 origin_z = 3; 436 | optional uint32 volume = 4; 437 | optional float delay_value = 5; 438 | optional int32 sequence_number = 6; 439 | optional int32 entity_index = 7; 440 | optional int32 channel = 8; 441 | optional int32 pitch = 9; 442 | optional int32 flags = 10; 443 | optional uint32 sound_num = 11; 444 | optional fixed32 sound_num_handle = 12; 445 | optional int32 speaker_entity = 13; 446 | optional int32 random_seed = 14; 447 | optional int32 sound_level = 15; 448 | optional bool is_sentence = 16; 449 | optional bool is_ambient = 17; 450 | } 451 | 452 | optional bool reliable_sound = 1; 453 | repeated .CSVCMsg_Sounds.sounddata_t sounds = 2; 454 | } 455 | 456 | message CSVCMsg_EntityMsg { 457 | optional int32 ent_index = 1; 458 | optional int32 class_id = 2; 459 | optional bytes ent_data = 3; 460 | } 461 | 462 | message CSVCMsg_CmdKeyValues { 463 | optional bytes keyvalues = 1; 464 | } 465 | 466 | message CSVCMsg_EncryptedData { 467 | optional bytes encrypted = 1; 468 | optional int32 key_type = 2; 469 | } 470 | 471 | message CSVCMsg_HltvReplay { 472 | optional int32 delay = 1; 473 | optional int32 primary_target = 2; 474 | optional int32 replay_stop_at = 3; 475 | optional int32 replay_start_at = 4; 476 | optional int32 replay_slowdown_begin = 5; 477 | optional int32 replay_slowdown_end = 6; 478 | optional float replay_slowdown_rate = 7; 479 | } 480 | 481 | message CCLCMsg_HltvReplay { 482 | optional int32 request = 1; 483 | optional float slowdown_length = 2; 484 | optional float slowdown_rate = 3; 485 | optional int32 primary_target_ent_index = 4; 486 | optional float event_time = 5; 487 | } 488 | 489 | message CSVCMsg_Broadcast_Command { 490 | optional string cmd = 1; 491 | } 492 | 493 | -------------------------------------------------------------------------------- /demoparser/protobufs/network_connection.proto: -------------------------------------------------------------------------------- 1 | import "google/protobuf/descriptor.proto"; 2 | 3 | option cc_generic_services = false; 4 | 5 | extend .google.protobuf.EnumValueOptions { 6 | optional string network_connection_token = 50500; 7 | } 8 | 9 | enum ENetworkDisconnectionReason { 10 | NETWORK_DISCONNECT_INVALID = 0; 11 | NETWORK_DISCONNECT_SHUTDOWN = 1; 12 | NETWORK_DISCONNECT_DISCONNECT_BY_USER = 2 [(network_connection_token) = "#GameUI_Disconnect_User"]; 13 | NETWORK_DISCONNECT_DISCONNECT_BY_SERVER = 3 [(network_connection_token) = "#GameUI_Disconnect_Server"]; 14 | NETWORK_DISCONNECT_LOST = 4 [(network_connection_token) = "#GameUI_Disconnect_ConnectionLost"]; 15 | NETWORK_DISCONNECT_OVERFLOW = 5 [(network_connection_token) = "#GameUI_Disconnect_ConnectionOverflow"]; 16 | NETWORK_DISCONNECT_STEAM_BANNED = 6 [(network_connection_token) = "#GameUI_Disconnect_SteamIDBanned"]; 17 | NETWORK_DISCONNECT_STEAM_INUSE = 7 [(network_connection_token) = "#GameUI_Disconnect_SteamIDInUse"]; 18 | NETWORK_DISCONNECT_STEAM_TICKET = 8 [(network_connection_token) = "#GameUI_Disconnect_SteamTicket"]; 19 | NETWORK_DISCONNECT_STEAM_LOGON = 9 [(network_connection_token) = "#GameUI_Disconnect_SteamLogon"]; 20 | NETWORK_DISCONNECT_STEAM_AUTHCANCELLED = 10 [(network_connection_token) = "#GameUI_Disconnect_SteamLogon"]; 21 | NETWORK_DISCONNECT_STEAM_AUTHALREADYUSED = 11 [(network_connection_token) = "#GameUI_Disconnect_SteamLogon"]; 22 | NETWORK_DISCONNECT_STEAM_AUTHINVALID = 12 [(network_connection_token) = "#GameUI_Disconnect_SteamLogon"]; 23 | NETWORK_DISCONNECT_STEAM_VACBANSTATE = 13 [(network_connection_token) = "#GameUI_Disconnect_SteamVAC"]; 24 | NETWORK_DISCONNECT_STEAM_LOGGED_IN_ELSEWHERE = 14 [(network_connection_token) = "#GameUI_Disconnect_SteamInUse"]; 25 | NETWORK_DISCONNECT_STEAM_VAC_CHECK_TIMEDOUT = 15 [(network_connection_token) = "#GameUI_Disconnect_SteamTimeOut"]; 26 | NETWORK_DISCONNECT_STEAM_DROPPED = 16 [(network_connection_token) = "#GameUI_Disconnect_SteamDropped"]; 27 | NETWORK_DISCONNECT_STEAM_OWNERSHIP = 17 [(network_connection_token) = "#GameUI_Disconnect_SteamOwnership"]; 28 | NETWORK_DISCONNECT_SERVERINFO_OVERFLOW = 18 [(network_connection_token) = "#GameUI_Disconnect_ServerInfoOverflow"]; 29 | NETWORK_DISCONNECT_TICKMSG_OVERFLOW = 19 [(network_connection_token) = "#GameUI_Disconnect_TickMessage"]; 30 | NETWORK_DISCONNECT_STRINGTABLEMSG_OVERFLOW = 20 [(network_connection_token) = "#GameUI_Disconnect_StringTableMessage"]; 31 | NETWORK_DISCONNECT_DELTAENTMSG_OVERFLOW = 21 [(network_connection_token) = "#GameUI_Disconnect_DeltaEntMessage"]; 32 | NETWORK_DISCONNECT_TEMPENTMSG_OVERFLOW = 22 [(network_connection_token) = "#GameUI_Disconnect_TempEntMessage"]; 33 | NETWORK_DISCONNECT_SOUNDSMSG_OVERFLOW = 23 [(network_connection_token) = "#GameUI_Disconnect_SoundsMessage"]; 34 | NETWORK_DISCONNECT_SNAPSHOTOVERFLOW = 24 [(network_connection_token) = "#GameUI_Disconnect_SnapshotOverflow"]; 35 | NETWORK_DISCONNECT_SNAPSHOTERROR = 25 [(network_connection_token) = "#GameUI_Disconnect_SnapshotError"]; 36 | NETWORK_DISCONNECT_RELIABLEOVERFLOW = 26 [(network_connection_token) = "#GameUI_Disconnect_ReliableOverflow"]; 37 | NETWORK_DISCONNECT_BADDELTATICK = 27 [(network_connection_token) = "#GameUI_Disconnect_BadClientDeltaTick"]; 38 | NETWORK_DISCONNECT_NOMORESPLITS = 28 [(network_connection_token) = "#GameUI_Disconnect_NoMoreSplits"]; 39 | NETWORK_DISCONNECT_TIMEDOUT = 29 [(network_connection_token) = "#GameUI_Disconnect_TimedOut"]; 40 | NETWORK_DISCONNECT_DISCONNECTED = 30 [(network_connection_token) = "#GameUI_Disconnect_Disconnected"]; 41 | NETWORK_DISCONNECT_LEAVINGSPLIT = 31 [(network_connection_token) = "#GameUI_Disconnect_LeavingSplit"]; 42 | NETWORK_DISCONNECT_DIFFERENTCLASSTABLES = 32 [(network_connection_token) = "#GameUI_Disconnect_DifferentClassTables"]; 43 | NETWORK_DISCONNECT_BADRELAYPASSWORD = 33 [(network_connection_token) = "#GameUI_Disconnect_BadRelayPassword"]; 44 | NETWORK_DISCONNECT_BADSPECTATORPASSWORD = 34 [(network_connection_token) = "#GameUI_Disconnect_BadSpectatorPassword"]; 45 | NETWORK_DISCONNECT_HLTVRESTRICTED = 35 [(network_connection_token) = "#GameUI_Disconnect_HLTVRestricted"]; 46 | NETWORK_DISCONNECT_NOSPECTATORS = 36 [(network_connection_token) = "#GameUI_Disconnect_NoSpectators"]; 47 | NETWORK_DISCONNECT_HLTVUNAVAILABLE = 37 [(network_connection_token) = "#GameUI_Disconnect_HLTVUnavailable"]; 48 | NETWORK_DISCONNECT_HLTVSTOP = 38 [(network_connection_token) = "#GameUI_Disconnect_HLTVStop"]; 49 | NETWORK_DISCONNECT_KICKED = 39 [(network_connection_token) = "#GameUI_Disconnect_Kicked"]; 50 | NETWORK_DISCONNECT_BANADDED = 40 [(network_connection_token) = "#GameUI_Disconnect_BanAdded"]; 51 | NETWORK_DISCONNECT_KICKBANADDED = 41 [(network_connection_token) = "#GameUI_Disconnect_KickBanAdded"]; 52 | NETWORK_DISCONNECT_HLTVDIRECT = 42 [(network_connection_token) = "#GameUI_Disconnect_HLTVDirect"]; 53 | NETWORK_DISCONNECT_PURESERVER_CLIENTEXTRA = 43 [(network_connection_token) = "#GameUI_Disconnect_PureServer_ClientExtra"]; 54 | NETWORK_DISCONNECT_PURESERVER_MISMATCH = 44 [(network_connection_token) = "#GameUI_Disconnect_PureServer_Mismatch"]; 55 | NETWORK_DISCONNECT_USERCMD = 45 [(network_connection_token) = "#GameUI_Disconnect_UserCmd"]; 56 | NETWORK_DISCONNECT_REJECTED_BY_GAME = 46 [(network_connection_token) = "#GameUI_Disconnect_RejectedByGame"]; 57 | NETWORK_DISCONNECT_MESSAGE_PARSE_ERROR = 47 [(network_connection_token) = "#GameUI_Disconnect_MessageParseError"]; 58 | NETWORK_DISCONNECT_INVALID_MESSAGE_ERROR = 48 [(network_connection_token) = "#GameUI_Disconnect_InvalidMessageError"]; 59 | NETWORK_DISCONNECT_BAD_SERVER_PASSWORD = 49 [(network_connection_token) = "#GameUI_Disconnect_BadServerPassword"]; 60 | NETWORK_DISCONNECT_DIRECT_CONNECT_RESERVATION = 50 [(network_connection_token) = "#GameUI_Disconnect_DirectConnectReservation"]; 61 | NETWORK_DISCONNECT_CONNECTION_FAILURE = 51 [(network_connection_token) = "#GameUI_Disconnect_ConnectionFailure"]; 62 | NETWORK_DISCONNECT_NO_PEER_GROUP_HANDLERS = 52 [(network_connection_token) = "#GameUI_Disconnect_NoPeerGroupHandlers"]; 63 | NETWORK_DISCONNECT_RECONNECTION = 53 [(network_connection_token) = "#GameUI_Disconnect_Reconnection"]; 64 | NETWORK_DISCONNECT_CONNECTION_CLOSING = 54 [(network_connection_token) = "#GameUI_Disconnect_ConnectionClosing"]; 65 | NETWORK_DISCONNECT_NO_GOTV_RELAYS_AVAILABLE = 55 [(network_connection_token) = "#GameUI_Disconnect_NoGOTVRelaysAvailable"]; 66 | NETWORK_DISCONNECT_SESSION_MIGRATED = 56 [(network_connection_token) = "#GameUI_Disconnect_SessionMigrated"]; 67 | NETWORK_DISCONNECT_VERYLARGETRANSFEROVERFLOW = 57 [(network_connection_token) = "#GameUI_Disconnect_VeryLargeTransferOverflow"]; 68 | NETWORK_DISCONNECT_SENDNETOVERFLOW = 58 [(network_connection_token) = "#GameUI_Disconnect_SendNetOverflow"]; 69 | NETWORK_DISCONNECT_PLAYER_REMOVED_FROM_HOST_SESSION = 59 [(network_connection_token) = "#GameUI_Disconnect_PlayerRemovedFromHostSession"]; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /demoparser/protobufs/steamdatagram_messages.proto: -------------------------------------------------------------------------------- 1 | option cc_generic_services = false; 2 | 3 | enum ESteamDatagramMsgID { 4 | k_ESteamDatagramMsg_RouterPingRequest = 1; 5 | k_ESteamDatagramMsg_RouterPingReply = 2; 6 | k_ESteamDatagramMsg_GameserverPingRequest = 3; 7 | k_ESteamDatagramMsg_GameserverPingReply = 4; 8 | k_ESteamDatagramMsg_GameserverSessionRequest = 5; 9 | k_ESteamDatagramMsg_GameserverSessionEstablished = 6; 10 | k_ESteamDatagramMsg_NoSession = 7; 11 | k_ESteamDatagramMsg_Diagnostic = 8; 12 | k_ESteamDatagramMsg_DataClientToRouter = 9; 13 | k_ESteamDatagramMsg_DataRouterToServer = 10; 14 | k_ESteamDatagramMsg_DataServerToRouter = 11; 15 | k_ESteamDatagramMsg_DataRouterToClient = 12; 16 | k_ESteamDatagramMsg_Stats = 13; 17 | k_ESteamDatagramMsg_ClientPingSampleRequest = 14; 18 | k_ESteamDatagramMsg_ClientPingSampleReply = 15; 19 | k_ESteamDatagramMsg_ClientToRouterSwitchedPrimary = 16; 20 | } 21 | 22 | message CMsgSteamDatagramRouterPingReply { 23 | optional fixed32 client_timestamp = 1; 24 | repeated fixed32 latency_datacenter_ids = 2 [packed = true]; 25 | repeated uint32 latency_ping_ms = 3 [packed = true]; 26 | optional fixed32 your_public_ip = 4; 27 | optional fixed32 server_time = 5; 28 | optional fixed64 challenge = 6; 29 | optional uint32 seconds_until_shutdown = 7; 30 | optional fixed32 client_cookie = 8; 31 | } 32 | 33 | message CMsgSteamDatagramGameserverPing { 34 | optional uint32 client_session = 1; 35 | optional fixed64 client_steam_id = 2; 36 | optional fixed32 client_timestamp = 3; 37 | optional fixed32 router_timestamp = 4; 38 | optional uint32 router_gameserver_latency = 5; 39 | optional uint32 seq_number_router = 6; 40 | optional uint32 seq_number_e2e = 7; 41 | } 42 | 43 | message CMsgSteamDatagramGameServerAuthTicket { 44 | message ExtraField { 45 | optional string name = 1; 46 | optional string string_value = 2; 47 | optional sint32 int32_value = 3; 48 | optional fixed32 fixed32_value = 4; 49 | optional fixed64 fixed64_value = 5; 50 | } 51 | 52 | optional fixed32 time_expiry = 1; 53 | optional fixed64 authorized_steam_id = 2; 54 | optional fixed32 authorized_public_ip = 3; 55 | optional fixed64 gameserver_steam_id = 4; 56 | optional fixed64 gameserver_net_id = 5; 57 | optional bytes signature = 6; 58 | optional uint32 app_id = 7; 59 | repeated .CMsgSteamDatagramGameServerAuthTicket.ExtraField extra_fields = 8; 60 | } 61 | 62 | message CMsgSteamDatagramGameserverSessionRequest { 63 | optional .CMsgSteamDatagramGameServerAuthTicket ticket = 1; 64 | optional fixed32 challenge_time = 3; 65 | optional fixed64 challenge = 4; 66 | optional fixed32 client_cookie = 5; 67 | } 68 | 69 | message CMsgSteamDatagramGameserverSessionEstablished { 70 | optional fixed32 client_cookie = 1; 71 | optional fixed64 gameserver_steam_id = 3; 72 | optional uint32 seconds_until_shutdown = 4; 73 | } 74 | 75 | message CMsgSteamDatagramNoSession { 76 | optional fixed32 client_cookie = 7; 77 | optional fixed32 your_public_ip = 2; 78 | optional fixed32 server_time = 3; 79 | optional fixed64 challenge = 4; 80 | optional uint32 seconds_until_shutdown = 5; 81 | } 82 | 83 | message CMsgSteamDatagramDiagnostic { 84 | optional uint32 severity = 1; 85 | optional string text = 2; 86 | } 87 | 88 | message CMsgSteamDatagramDataCenterState { 89 | message Server { 90 | optional string address = 1; 91 | optional uint32 ping_ms = 2; 92 | } 93 | 94 | message DataCenter { 95 | optional string code = 1; 96 | repeated .CMsgSteamDatagramDataCenterState.Server server_sample = 2; 97 | } 98 | 99 | repeated .CMsgSteamDatagramDataCenterState.DataCenter data_centers = 1; 100 | } 101 | 102 | message CMsgSteamDatagramLinkInstantaneousStats { 103 | optional uint32 out_packets_per_sec_x10 = 1; 104 | optional uint32 out_bytes_per_sec = 2; 105 | optional uint32 in_packets_per_sec_x10 = 3; 106 | optional uint32 in_bytes_per_sec = 4; 107 | optional uint32 ping_ms = 5; 108 | optional uint32 packets_dropped_pct = 6; 109 | optional uint32 packets_weird_sequence_pct = 7; 110 | } 111 | 112 | message CMsgSteamDatagramLinkLifetimeStats { 113 | optional uint64 packets_sent = 3; 114 | optional uint64 kb_sent = 4; 115 | optional uint64 packets_recv = 5; 116 | optional uint64 kb_recv = 6; 117 | optional uint64 packets_recv_sequenced = 7; 118 | optional uint64 packets_recv_dropped = 8; 119 | optional uint64 packets_recv_out_of_order = 9; 120 | optional uint64 packets_recv_duplicate = 10; 121 | optional uint64 packets_recv_lurch = 11; 122 | } 123 | 124 | message CMsgSteamDatagramConnectionQuality { 125 | optional .CMsgSteamDatagramLinkInstantaneousStats instantaneous = 1; 126 | optional .CMsgSteamDatagramLinkLifetimeStats lifetime = 2; 127 | } 128 | 129 | message CMsgSteamDatagramConnectionStatsClientToRouter { 130 | optional .CMsgSteamDatagramConnectionQuality c2r = 1; 131 | optional .CMsgSteamDatagramConnectionQuality c2s = 2; 132 | optional fixed32 client_timestamp = 3; 133 | optional fixed32 client_cookie = 8; 134 | optional uint32 seq_num_c2r = 9; 135 | optional uint32 seq_num_c2s = 10; 136 | } 137 | 138 | message CMsgSteamDatagramConnectionStatsRouterToClient { 139 | optional .CMsgSteamDatagramConnectionQuality r2c = 1; 140 | optional .CMsgSteamDatagramConnectionQuality s2c = 2; 141 | optional fixed32 client_timestamp_from_router = 3; 142 | optional fixed32 client_timestamp_from_server = 4; 143 | optional uint32 router_gameserver_latency = 5; 144 | optional uint32 seconds_until_shutdown = 6; 145 | optional fixed32 client_cookie = 7; 146 | optional uint32 seq_num_r2c = 8; 147 | optional uint32 seq_num_s2c = 9; 148 | } 149 | 150 | message CMsgSteamDatagramConnectionStatsRouterToServer { 151 | optional .CMsgSteamDatagramConnectionQuality r2s = 1; 152 | optional .CMsgSteamDatagramConnectionQuality c2s = 2; 153 | optional fixed32 client_timestamp = 3; 154 | optional fixed32 router_timestamp = 4; 155 | optional uint32 seq_num_r2s = 5; 156 | optional uint32 seq_num_c2s = 6; 157 | optional fixed64 client_steam_id = 7; 158 | optional uint32 client_session_id = 8; 159 | } 160 | 161 | message CMsgSteamDatagramConnectionStatsServerToRouter { 162 | optional .CMsgSteamDatagramConnectionQuality s2r = 1; 163 | optional .CMsgSteamDatagramConnectionQuality s2c = 2; 164 | optional uint32 seq_num_s2r = 3; 165 | optional uint32 seq_num_s2c = 4; 166 | optional fixed64 client_steam_id = 5; 167 | optional uint32 client_session_id = 6; 168 | } 169 | 170 | message CMsgSteamDatagramClientPingSampleRequest { 171 | optional fixed32 client_cookie = 1; 172 | } 173 | 174 | message CMsgSteamDatagramClientPingSampleReply { 175 | message RoutingCluster { 176 | optional fixed32 id = 1; 177 | optional uint32 front_ping_ms = 2; 178 | optional uint32 e2e_ping_ms = 3; 179 | } 180 | 181 | optional fixed32 client_cookie = 1; 182 | repeated .CMsgSteamDatagramClientPingSampleReply.RoutingCluster routing_clusters = 2; 183 | } 184 | 185 | message CMsgSteamDatagramClientSwitchedPrimary { 186 | message RouterQuality { 187 | optional uint32 score = 1; 188 | optional uint32 front_ping = 2; 189 | optional uint32 back_ping = 3; 190 | optional uint32 seconds_until_down = 4; 191 | } 192 | 193 | optional fixed32 client_cookie = 1; 194 | optional fixed32 from_ip = 2; 195 | optional uint32 from_port = 3; 196 | optional fixed32 from_router_cluster = 4; 197 | optional uint32 from_active_time = 5; 198 | optional uint32 from_active_packets_recv = 6; 199 | optional string from_dropped_reason = 7; 200 | optional uint32 gap_ms = 8; 201 | optional .CMsgSteamDatagramClientSwitchedPrimary.RouterQuality from_quality_now = 9; 202 | optional .CMsgSteamDatagramClientSwitchedPrimary.RouterQuality to_quality_now = 10; 203 | optional .CMsgSteamDatagramClientSwitchedPrimary.RouterQuality from_quality_then = 11; 204 | optional .CMsgSteamDatagramClientSwitchedPrimary.RouterQuality to_quality_then = 12; 205 | } 206 | 207 | -------------------------------------------------------------------------------- /demoparser/protobufs/steammessages.proto: -------------------------------------------------------------------------------- 1 | import "google/protobuf/descriptor.proto"; 2 | 3 | option optimize_for = SPEED; 4 | option cc_generic_services = false; 5 | 6 | extend .google.protobuf.FieldOptions { 7 | optional bool key_field = 60000 [default = false]; 8 | } 9 | 10 | extend .google.protobuf.MessageOptions { 11 | optional int32 msgpool_soft_limit = 60000 [default = 32]; 12 | optional int32 msgpool_hard_limit = 60001 [default = 384]; 13 | } 14 | 15 | enum GCProtoBufMsgSrc { 16 | GCProtoBufMsgSrc_Unspecified = 0; 17 | GCProtoBufMsgSrc_FromSystem = 1; 18 | GCProtoBufMsgSrc_FromSteamID = 2; 19 | GCProtoBufMsgSrc_FromGC = 3; 20 | GCProtoBufMsgSrc_ReplySystem = 4; 21 | } 22 | 23 | message CMsgProtoBufHeader { 24 | option (msgpool_soft_limit) = 256; 25 | option (msgpool_hard_limit) = 1024; 26 | optional fixed64 client_steam_id = 1; 27 | optional int32 client_session_id = 2; 28 | optional uint32 source_app_id = 3; 29 | optional fixed64 job_id_source = 10 [default = 18446744073709551615]; 30 | optional fixed64 job_id_target = 11 [default = 18446744073709551615]; 31 | optional string target_job_name = 12; 32 | optional int32 eresult = 13 [default = 2]; 33 | optional string error_message = 14; 34 | optional uint32 ip = 15; 35 | optional .GCProtoBufMsgSrc gc_msg_src = 200 [default = GCProtoBufMsgSrc_Unspecified]; 36 | optional uint32 gc_dir_index_source = 201; 37 | } 38 | 39 | message CMsgWebAPIKey { 40 | optional uint32 status = 1 [default = 255]; 41 | optional uint32 account_id = 2 [default = 0]; 42 | optional uint32 publisher_group_id = 3 [default = 0]; 43 | optional uint32 key_id = 4; 44 | optional string domain = 5; 45 | } 46 | 47 | message CMsgHttpRequest { 48 | message RequestHeader { 49 | optional string name = 1; 50 | optional string value = 2; 51 | } 52 | 53 | message QueryParam { 54 | optional string name = 1; 55 | optional bytes value = 2; 56 | } 57 | 58 | optional uint32 request_method = 1; 59 | optional string hostname = 2; 60 | optional string url = 3; 61 | repeated .CMsgHttpRequest.RequestHeader headers = 4; 62 | repeated .CMsgHttpRequest.QueryParam get_params = 5; 63 | repeated .CMsgHttpRequest.QueryParam post_params = 6; 64 | optional bytes body = 7; 65 | optional uint32 absolute_timeout = 8; 66 | } 67 | 68 | message CMsgWebAPIRequest { 69 | optional string UNUSED_job_name = 1; 70 | optional string interface_name = 2; 71 | optional string method_name = 3; 72 | optional uint32 version = 4; 73 | optional .CMsgWebAPIKey api_key = 5; 74 | optional .CMsgHttpRequest request = 6; 75 | optional uint32 routing_app_id = 7; 76 | } 77 | 78 | message CMsgHttpResponse { 79 | message ResponseHeader { 80 | optional string name = 1; 81 | optional string value = 2; 82 | } 83 | 84 | optional uint32 status_code = 1; 85 | repeated .CMsgHttpResponse.ResponseHeader headers = 2; 86 | optional bytes body = 3; 87 | } 88 | 89 | message CMsgAMFindAccounts { 90 | optional uint32 search_type = 1; 91 | optional string search_string = 2; 92 | } 93 | 94 | message CMsgAMFindAccountsResponse { 95 | repeated fixed64 steam_id = 1; 96 | } 97 | 98 | message CMsgNotifyWatchdog { 99 | optional uint32 source = 1; 100 | optional uint32 alert_type = 2; 101 | optional uint32 alert_destination = 3; 102 | optional bool critical = 4; 103 | optional uint32 time = 5; 104 | optional uint32 appid = 6; 105 | optional string text = 7; 106 | } 107 | 108 | message CMsgAMGetLicenses { 109 | optional fixed64 steamid = 1; 110 | } 111 | 112 | message CMsgPackageLicense { 113 | optional uint32 package_id = 1; 114 | optional uint32 time_created = 2; 115 | optional uint32 owner_id = 3; 116 | } 117 | 118 | message CMsgAMGetLicensesResponse { 119 | repeated .CMsgPackageLicense license = 1; 120 | optional uint32 result = 2; 121 | } 122 | 123 | message CMsgAMGetUserGameStats { 124 | optional fixed64 steam_id = 1; 125 | optional fixed64 game_id = 2; 126 | repeated uint32 stats = 3; 127 | } 128 | 129 | message CMsgAMGetUserGameStatsResponse { 130 | message Stats { 131 | optional uint32 stat_id = 1; 132 | optional uint32 stat_value = 2; 133 | } 134 | 135 | message Achievement_Blocks { 136 | optional uint32 achievement_id = 1; 137 | optional uint32 achievement_bit_id = 2; 138 | optional fixed32 unlock_time = 3; 139 | } 140 | 141 | optional fixed64 steam_id = 1; 142 | optional fixed64 game_id = 2; 143 | optional int32 eresult = 3 [default = 2]; 144 | repeated .CMsgAMGetUserGameStatsResponse.Stats stats = 4; 145 | repeated .CMsgAMGetUserGameStatsResponse.Achievement_Blocks achievement_blocks = 5; 146 | } 147 | 148 | message CMsgGCGetCommandList { 149 | optional uint32 app_id = 1; 150 | optional string command_prefix = 2; 151 | } 152 | 153 | message CMsgGCGetCommandListResponse { 154 | repeated string command_name = 1; 155 | } 156 | 157 | message CGCMsgMemCachedGet { 158 | repeated string keys = 1; 159 | } 160 | 161 | message CGCMsgMemCachedGetResponse { 162 | message ValueTag { 163 | optional bool found = 1; 164 | optional bytes value = 2; 165 | } 166 | 167 | repeated .CGCMsgMemCachedGetResponse.ValueTag values = 1; 168 | } 169 | 170 | message CGCMsgMemCachedSet { 171 | message KeyPair { 172 | optional string name = 1; 173 | optional bytes value = 2; 174 | } 175 | 176 | repeated .CGCMsgMemCachedSet.KeyPair keys = 1; 177 | } 178 | 179 | message CGCMsgMemCachedDelete { 180 | repeated string keys = 1; 181 | } 182 | 183 | message CGCMsgMemCachedStats { 184 | } 185 | 186 | message CGCMsgMemCachedStatsResponse { 187 | optional uint64 curr_connections = 1; 188 | optional uint64 cmd_get = 2; 189 | optional uint64 cmd_set = 3; 190 | optional uint64 cmd_flush = 4; 191 | optional uint64 get_hits = 5; 192 | optional uint64 get_misses = 6; 193 | optional uint64 delete_hits = 7; 194 | optional uint64 delete_misses = 8; 195 | optional uint64 bytes_read = 9; 196 | optional uint64 bytes_written = 10; 197 | optional uint64 limit_maxbytes = 11; 198 | optional uint64 curr_items = 12; 199 | optional uint64 evictions = 13; 200 | optional uint64 bytes = 14; 201 | } 202 | 203 | message CGCMsgSQLStats { 204 | optional uint32 schema_catalog = 1; 205 | } 206 | 207 | message CGCMsgSQLStatsResponse { 208 | optional uint32 threads = 1; 209 | optional uint32 threads_connected = 2; 210 | optional uint32 threads_active = 3; 211 | optional uint32 operations_submitted = 4; 212 | optional uint32 prepared_statements_executed = 5; 213 | optional uint32 non_prepared_statements_executed = 6; 214 | optional uint32 deadlock_retries = 7; 215 | optional uint32 operations_timed_out_in_queue = 8; 216 | optional uint32 errors = 9; 217 | } 218 | 219 | message CMsgAMAddFreeLicense { 220 | optional fixed64 steamid = 1; 221 | optional uint32 ip_public = 2; 222 | optional uint32 packageid = 3; 223 | optional string store_country_code = 4; 224 | } 225 | 226 | message CMsgAMAddFreeLicenseResponse { 227 | optional int32 eresult = 1 [default = 2]; 228 | optional int32 purchase_result_detail = 2; 229 | optional fixed64 transid = 3; 230 | } 231 | 232 | message CGCMsgGetIPLocation { 233 | repeated fixed32 ips = 1; 234 | } 235 | 236 | message CIPLocationInfo { 237 | optional uint32 ip = 1; 238 | optional float latitude = 2; 239 | optional float longitude = 3; 240 | optional string country = 4; 241 | optional string state = 5; 242 | optional string city = 6; 243 | } 244 | 245 | message CGCMsgGetIPLocationResponse { 246 | repeated .CIPLocationInfo infos = 1; 247 | } 248 | 249 | message CGCMsgSystemStatsSchema { 250 | optional uint32 gc_app_id = 1; 251 | optional bytes schema_kv = 2; 252 | } 253 | 254 | message CGCMsgGetSystemStats { 255 | } 256 | 257 | message CGCMsgGetSystemStatsResponse { 258 | optional uint32 gc_app_id = 1; 259 | optional bytes stats_kv = 2; 260 | optional uint32 active_jobs = 3; 261 | optional uint32 yielding_jobs = 4; 262 | optional uint32 user_sessions = 5; 263 | optional uint32 game_server_sessions = 6; 264 | optional uint32 socaches = 7; 265 | optional uint32 socaches_to_unload = 8; 266 | optional uint32 socaches_loading = 9; 267 | optional uint32 writeback_queue = 10; 268 | optional uint32 steamid_locks = 11; 269 | optional uint32 logon_queue = 12; 270 | optional uint32 logon_jobs = 13; 271 | } 272 | 273 | message CMsgAMSendEmail { 274 | message ReplacementToken { 275 | optional string token_name = 1; 276 | optional string token_value = 2; 277 | } 278 | 279 | message PersonaNameReplacementToken { 280 | optional fixed64 steamid = 1; 281 | optional string token_name = 2; 282 | } 283 | 284 | optional fixed64 steamid = 1; 285 | optional uint32 email_msg_type = 2; 286 | optional uint32 email_format = 3; 287 | repeated .CMsgAMSendEmail.PersonaNameReplacementToken persona_name_tokens = 5; 288 | optional uint32 source_gc = 6; 289 | repeated .CMsgAMSendEmail.ReplacementToken tokens = 7; 290 | } 291 | 292 | message CMsgAMSendEmailResponse { 293 | optional uint32 eresult = 1 [default = 2]; 294 | } 295 | 296 | message CMsgGCGetEmailTemplate { 297 | optional uint32 app_id = 1; 298 | optional uint32 email_msg_type = 2; 299 | optional int32 email_lang = 3; 300 | optional int32 email_format = 4; 301 | } 302 | 303 | message CMsgGCGetEmailTemplateResponse { 304 | optional uint32 eresult = 1 [default = 2]; 305 | optional bool template_exists = 2; 306 | optional string template = 3; 307 | } 308 | 309 | message CMsgAMGrantGuestPasses2 { 310 | optional fixed64 steam_id = 1; 311 | optional uint32 package_id = 2; 312 | optional int32 passes_to_grant = 3; 313 | optional int32 days_to_expiration = 4; 314 | optional int32 action = 5; 315 | } 316 | 317 | message CMsgAMGrantGuestPasses2Response { 318 | optional int32 eresult = 1 [default = 2]; 319 | optional int32 passes_granted = 2 [default = 0]; 320 | } 321 | 322 | message CGCSystemMsg_GetAccountDetails { 323 | option (msgpool_soft_limit) = 128; 324 | option (msgpool_hard_limit) = 512; 325 | optional fixed64 steamid = 1; 326 | optional uint32 appid = 2; 327 | } 328 | 329 | message CGCSystemMsg_GetAccountDetails_Response { 330 | option (msgpool_soft_limit) = 128; 331 | option (msgpool_hard_limit) = 512; 332 | optional uint32 eresult_deprecated = 1 [default = 2]; 333 | optional string account_name = 2; 334 | optional string persona_name = 3; 335 | optional bool is_profile_public = 4; 336 | optional bool is_inventory_public = 5; 337 | optional bool is_vac_banned = 7; 338 | optional bool is_cyber_cafe = 8; 339 | optional bool is_school_account = 9; 340 | optional bool is_limited = 10; 341 | optional bool is_subscribed = 11; 342 | optional uint32 package = 12; 343 | optional bool is_free_trial_account = 13; 344 | optional uint32 free_trial_expiration = 14; 345 | optional bool is_low_violence = 15; 346 | optional bool is_account_locked_down = 16; 347 | optional bool is_community_banned = 17; 348 | optional bool is_trade_banned = 18; 349 | optional uint32 trade_ban_expiration = 19; 350 | optional uint32 accountid = 20; 351 | optional uint32 suspension_end_time = 21; 352 | optional string currency = 22; 353 | optional uint32 steam_level = 23; 354 | optional uint32 friend_count = 24; 355 | optional uint32 account_creation_time = 25; 356 | optional bool is_steamguard_enabled = 27; 357 | optional bool is_phone_verified = 28; 358 | optional bool is_two_factor_auth_enabled = 29; 359 | optional uint32 two_factor_enabled_time = 30; 360 | optional uint32 phone_verification_time = 31; 361 | optional uint64 phone_id = 33; 362 | optional bool is_phone_identifying = 34; 363 | optional uint32 rt_identity_linked = 35; 364 | optional uint32 rt_birth_date = 36; 365 | optional string txn_country_code = 37; 366 | } 367 | 368 | message CMsgGCGetPersonaNames { 369 | repeated fixed64 steamids = 1; 370 | } 371 | 372 | message CMsgGCGetPersonaNames_Response { 373 | message PersonaName { 374 | optional fixed64 steamid = 1; 375 | optional string persona_name = 2; 376 | } 377 | 378 | repeated .CMsgGCGetPersonaNames_Response.PersonaName succeeded_lookups = 1; 379 | repeated fixed64 failed_lookup_steamids = 2; 380 | } 381 | 382 | message CMsgGCCheckFriendship { 383 | optional fixed64 steamid_left = 1; 384 | optional fixed64 steamid_right = 2; 385 | } 386 | 387 | message CMsgGCCheckFriendship_Response { 388 | optional bool success = 1; 389 | optional bool found_friendship = 2; 390 | } 391 | 392 | message CMsgGCMsgMasterSetDirectory { 393 | message SubGC { 394 | optional uint32 dir_index = 1; 395 | optional string name = 2; 396 | optional string box = 3; 397 | optional string command_line = 4; 398 | optional string gc_binary = 5; 399 | } 400 | 401 | optional uint32 master_dir_index = 1; 402 | repeated .CMsgGCMsgMasterSetDirectory.SubGC dir = 2; 403 | } 404 | 405 | message CMsgGCMsgMasterSetDirectory_Response { 406 | optional int32 eresult = 1 [default = 2]; 407 | } 408 | 409 | message CMsgGCMsgWebAPIJobRequestForwardResponse { 410 | optional uint32 dir_index = 1; 411 | } 412 | 413 | message CGCSystemMsg_GetPurchaseTrust_Request { 414 | optional fixed64 steamid = 1; 415 | } 416 | 417 | message CGCSystemMsg_GetPurchaseTrust_Response { 418 | optional bool has_prior_purchase_history = 1; 419 | optional bool has_no_recent_password_resets = 2; 420 | optional bool is_wallet_cash_trusted = 3; 421 | optional uint32 time_all_trusted = 4; 422 | } 423 | 424 | message CMsgGCHAccountVacStatusChange { 425 | optional fixed64 steam_id = 1; 426 | optional uint32 app_id = 2; 427 | optional uint32 rtime_vacban_starts = 3; 428 | optional bool is_banned_now = 4; 429 | optional bool is_banned_future = 5; 430 | } 431 | 432 | message CMsgGCGetPartnerAccountLink { 433 | optional fixed64 steamid = 1; 434 | } 435 | 436 | message CMsgGCGetPartnerAccountLink_Response { 437 | optional uint32 pwid = 1; 438 | optional uint32 nexonid = 2; 439 | } 440 | 441 | message CMsgGCRoutingInfo { 442 | enum RoutingMethod { 443 | RANDOM = 0; 444 | DISCARD = 1; 445 | CLIENT_STEAMID = 2; 446 | PROTOBUF_FIELD_UINT64 = 3; 447 | WEBAPI_PARAM_UINT64 = 4; 448 | } 449 | 450 | repeated uint32 dir_index = 1; 451 | optional .CMsgGCRoutingInfo.RoutingMethod method = 2 [default = RANDOM]; 452 | optional .CMsgGCRoutingInfo.RoutingMethod fallback = 3 [default = DISCARD]; 453 | optional uint32 protobuf_field = 4; 454 | optional string webapi_param = 5; 455 | } 456 | 457 | message CMsgGCMsgMasterSetWebAPIRouting { 458 | message Entry { 459 | optional string interface_name = 1; 460 | optional string method_name = 2; 461 | optional .CMsgGCRoutingInfo routing = 3; 462 | } 463 | 464 | repeated .CMsgGCMsgMasterSetWebAPIRouting.Entry entries = 1; 465 | } 466 | 467 | message CMsgGCMsgMasterSetClientMsgRouting { 468 | message Entry { 469 | optional uint32 msg_type = 1; 470 | optional .CMsgGCRoutingInfo routing = 2; 471 | } 472 | 473 | repeated .CMsgGCMsgMasterSetClientMsgRouting.Entry entries = 1; 474 | } 475 | 476 | message CMsgGCMsgMasterSetWebAPIRouting_Response { 477 | optional int32 eresult = 1 [default = 2]; 478 | } 479 | 480 | message CMsgGCMsgMasterSetClientMsgRouting_Response { 481 | optional int32 eresult = 1 [default = 2]; 482 | } 483 | 484 | message CMsgGCMsgSetOptions { 485 | message MessageRange { 486 | required uint32 low = 1; 487 | required uint32 high = 2; 488 | } 489 | 490 | enum Option { 491 | NOTIFY_USER_SESSIONS = 0; 492 | NOTIFY_SERVER_SESSIONS = 1; 493 | NOTIFY_ACHIEVEMENTS = 2; 494 | NOTIFY_VAC_ACTION = 3; 495 | } 496 | 497 | repeated .CMsgGCMsgSetOptions.Option options = 1; 498 | repeated .CMsgGCMsgSetOptions.MessageRange client_msg_ranges = 2; 499 | } 500 | 501 | message CMsgGCHUpdateSession { 502 | message ExtraField { 503 | optional string name = 1; 504 | optional string value = 2; 505 | } 506 | 507 | optional fixed64 steam_id = 1; 508 | optional uint32 app_id = 2; 509 | optional bool online = 3; 510 | optional fixed64 server_steam_id = 4; 511 | optional uint32 server_addr = 5; 512 | optional uint32 server_port = 6; 513 | optional uint32 os_type = 7; 514 | optional uint32 client_addr = 8; 515 | repeated .CMsgGCHUpdateSession.ExtraField extra_fields = 9; 516 | optional fixed64 owner_id = 10; 517 | optional uint32 cm_session_sysid = 11; 518 | optional uint32 cm_session_identifier = 12; 519 | repeated uint32 depot_ids = 13; 520 | } 521 | 522 | message CMsgNotificationOfSuspiciousActivity { 523 | message MultipleGameInstances { 524 | optional uint32 app_instance_count = 1; 525 | repeated fixed64 other_steamids = 2; 526 | } 527 | 528 | optional fixed64 steamid = 1; 529 | optional uint32 appid = 2; 530 | optional .CMsgNotificationOfSuspiciousActivity.MultipleGameInstances multiple_instances = 3; 531 | } 532 | 533 | message CMsgDPPartnerMicroTxns { 534 | message PartnerMicroTxn { 535 | optional uint32 init_time = 1; 536 | optional uint32 last_update_time = 2; 537 | optional uint64 txn_id = 3; 538 | optional uint32 account_id = 4; 539 | optional uint32 line_item = 5; 540 | optional uint64 item_id = 6; 541 | optional uint32 def_index = 7; 542 | optional uint64 price = 8; 543 | optional uint64 tax = 9; 544 | optional uint64 price_usd = 10; 545 | optional uint64 tax_usd = 11; 546 | optional uint32 purchase_type = 12; 547 | optional uint32 steam_txn_type = 13; 548 | optional string country_code = 14; 549 | optional string region_code = 15; 550 | optional int32 quantity = 16; 551 | optional uint64 ref_trans_id = 17; 552 | } 553 | 554 | message PartnerInfo { 555 | optional uint32 partner_id = 1; 556 | optional string partner_name = 2; 557 | optional string currency_code = 3; 558 | optional string currency_name = 4; 559 | } 560 | 561 | optional uint32 appid = 1; 562 | optional string gc_name = 2; 563 | optional .CMsgDPPartnerMicroTxns.PartnerInfo partner = 3; 564 | repeated .CMsgDPPartnerMicroTxns.PartnerMicroTxn transactions = 4; 565 | } 566 | 567 | message CMsgDPPartnerMicroTxnsResponse { 568 | enum EErrorCode { 569 | k_MsgValid = 0; 570 | k_MsgInvalidAppID = 1; 571 | k_MsgInvalidPartnerInfo = 2; 572 | k_MsgNoTransactions = 3; 573 | k_MsgSQLFailure = 4; 574 | k_MsgPartnerInfoDiscrepancy = 5; 575 | k_MsgTransactionInsertFailed = 7; 576 | k_MsgAlreadyRunning = 8; 577 | k_MsgInvalidTransactionData = 9; 578 | } 579 | 580 | optional uint32 eresult = 1 [default = 2]; 581 | optional .CMsgDPPartnerMicroTxnsResponse.EErrorCode eerrorcode = 2 [default = k_MsgValid]; 582 | } 583 | 584 | -------------------------------------------------------------------------------- /demoparser/protobufs/uifontfile_format.proto: -------------------------------------------------------------------------------- 1 | option optimize_for = SPEED; 2 | option cc_generic_services = false; 3 | 4 | message CUIFontFilePB { 5 | optional string font_file_name = 1; 6 | optional bytes opentype_font_data = 2; 7 | } 8 | 9 | message CUIFontFilePackagePB { 10 | message CUIEncryptedFontFilePB { 11 | optional bytes encrypted_contents = 1; 12 | } 13 | 14 | required uint32 package_version = 1; 15 | repeated .CUIFontFilePackagePB.CUIEncryptedFontFilePB encrypted_font_files = 2; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /demoparser/structures.py: -------------------------------------------------------------------------------- 1 | from suitcase.structure import Structure 2 | from suitcase.fields import FieldArray 3 | from suitcase.fields import Magic 4 | from suitcase.fields import SubstructureField 5 | from suitcase.fields import ULInt32 6 | from suitcase.fields import UBInt32 7 | from suitcase.fields import UBInt64 8 | from suitcase.fields import UBInt8 9 | from suitcase.fields import ULInt8 10 | 11 | from demoparser import consts 12 | from demoparser.fields import FixedLengthString 13 | from demoparser.fields import SLFloat32 14 | from demoparser.fields import UBInt32Sequence 15 | 16 | 17 | class DemoHeader(Structure): 18 | r"""1072 Byte header for .DEM file. 19 | 20 | This header has the following format: 21 | 22 | +-----------+---------------------------------------+ 23 | | Byte | Description | 24 | +===========+=======================================+ 25 | | 0-7 | Fixed string 'HL2DEMO\0'. | 26 | +-----------+---------------------------------------+ 27 | | 8-11 | Demo file protocol version. | 28 | +-----------+---------------------------------------+ 29 | | 12-15 | Network protocol version. | 30 | +-----------+---------------------------------------+ 31 | | 16-275 | Server name. | 32 | +-----------+---------------------------------------+ 33 | | 276-535 | Name of client who recorded the demo. | 34 | +-----------+---------------------------------------+ 35 | | 536-795 | Map name. | 36 | +-----------+---------------------------------------+ 37 | | 796-1055 | Game directory. | 38 | +-----------+---------------------------------------+ 39 | | 1056-1059 | Playback time in seconds. | 40 | +-----------+---------------------------------------+ 41 | | 1060-1063 | Number of ticks in demo. | 42 | +-----------+---------------------------------------+ 43 | | 1064-1067 | Number of frames in demo. | 44 | +-----------+---------------------------------------+ 45 | | 1068-1071 | Length of signon data. | 46 | +-----------+---------------------------------------+ 47 | """ 48 | header = Magic(b'HL2DEMO\x00') 49 | demo_protocol = ULInt32() 50 | network_protocol = ULInt32() 51 | server_name = FixedLengthString(consts.MAX_PATH) 52 | client_name = FixedLengthString(consts.MAX_PATH) 53 | map_name = FixedLengthString(consts.MAX_PATH) 54 | game_directory = FixedLengthString(consts.MAX_PATH) 55 | playback_time = SLFloat32() 56 | ticks = ULInt32() 57 | frames = ULInt32() 58 | signon_length = ULInt32() 59 | 60 | 61 | class CommandHeader(Structure): 62 | """Header for each command packet. 63 | 64 | .. _header-format: 65 | 66 | The header has the following format: 67 | 68 | +------+--------------+ 69 | | Byte | Description | 70 | +======+==============+ 71 | | 0 | Command ID | 72 | +------+--------------+ 73 | | 1-4 | Current tick | 74 | +------+--------------+ 75 | | 5 | Player ID | 76 | +------+--------------+ 77 | """ 78 | command = ULInt8() 79 | tick = ULInt32() 80 | player = ULInt8() 81 | 82 | 83 | class QAngle(Structure): 84 | pitch = SLFloat32() 85 | yaw = SLFloat32() 86 | roll = SLFloat32() 87 | 88 | 89 | class Vector(Structure): 90 | x = SLFloat32() 91 | y = SLFloat32() 92 | z = SLFloat32() 93 | 94 | 95 | class OriginViewAngles(Structure): 96 | view_origin = SubstructureField(Vector) 97 | view_angles = SubstructureField(QAngle) 98 | local_view_angles = SubstructureField(QAngle) 99 | 100 | 101 | class SplitCommandInfo(Structure): 102 | flags = ULInt32() 103 | original = SubstructureField(OriginViewAngles) 104 | resampled = SubstructureField(OriginViewAngles) 105 | 106 | 107 | class CommandInfo(Structure): 108 | commands = FieldArray(SplitCommandInfo) 109 | 110 | 111 | class UserInfo(Structure): 112 | """Player data. 113 | 114 | This structure has the following format: 115 | 116 | +---------+---------------------------------------+ 117 | | Byte | Description | 118 | +=========+=======================================+ 119 | | 0-7 | Version. Same for all players. | 120 | +---------+---------------------------------------+ 121 | | 8-15 | xuid. Some sort of unique ID. | 122 | +---------+---------------------------------------+ 123 | | 15-142 | Player name. | 124 | +---------+---------------------------------------+ 125 | | 143-146 | Local server user ID. | 126 | +---------+---------------------------------------+ 127 | | 147-179 | GUID | 128 | +---------+---------------------------------------+ 129 | | 180-183 | Friend's ID. | 130 | +---------+---------------------------------------+ 131 | | 184-312 | Friend's Name. | 132 | +---------+---------------------------------------+ 133 | | 313 | Is player a bot? | 134 | +---------+---------------------------------------+ 135 | | 314 | Is player an HLTV proxy? | 136 | +---------+---------------------------------------+ 137 | | 314-329 | Custom files CRC. | 138 | +---------+---------------------------------------+ 139 | | 330 | Numbre of files downloaded by server. | 140 | +---------+---------------------------------------+ 141 | | 331-335 | Entity index. | 142 | +---------+---------------------------------------+ 143 | | 336-340 | No idea. | 144 | +---------+---------------------------------------+ 145 | """ 146 | version = UBInt64() 147 | xuid = UBInt64() 148 | name = FixedLengthString(consts.MAX_PLAYER_NAME_LENGTH) 149 | user_id = UBInt32() 150 | guid = FixedLengthString(consts.SIGNED_GUID_LEN) 151 | friends_id = UBInt32() 152 | friends_name = FixedLengthString(consts.MAX_PLAYER_NAME_LENGTH) 153 | fake_player = UBInt8() 154 | is_hltv = UBInt8() 155 | custom_files = UBInt32Sequence(consts.MAX_CUSTOM_FILES) 156 | files_downloaded = UBInt8() 157 | entity_id = UBInt32() 158 | tbd = UBInt32() 159 | -------------------------------------------------------------------------------- /demoparser/util.pxd: -------------------------------------------------------------------------------- 1 | from demoparser.bitbuffer cimport Bitbuffer 2 | 3 | cpdef int read_field_index(Bitbuffer buf, int last_index, bint new_way) 4 | cpdef list parse_entity_update(Bitbuffer buf, object server_class) 5 | -------------------------------------------------------------------------------- /demoparser/util.pyx: -------------------------------------------------------------------------------- 1 | from demoparser.bitbuffer cimport Bitbuffer 2 | from demoparser.props cimport Decoder 3 | from demoparser.props cimport PropFlags 4 | from demoparser.props cimport PropTypes 5 | 6 | cpdef int read_field_index(Bitbuffer buf, int last_index, bint new_way): 7 | """Read index. 8 | 9 | This method determines the next index into the property 10 | list for a server class. 11 | 12 | :returns: Next index or -1 if no more indices 13 | """ 14 | cdef int ret = 0 15 | cdef unsigned int val = 0 16 | 17 | if new_way and buf.read_bit(): 18 | return last_index + 1 19 | 20 | if new_way and buf.read_bit(): 21 | ret = buf.read_uint_bits(3) 22 | else: 23 | ret = buf.read_uint_bits(7) 24 | val = ret & (32 | 64) 25 | 26 | if val == 32: 27 | ret = (ret & ~96) | (buf.read_uint_bits(2) << 5) 28 | assert ret >= 32 29 | elif val == 64: 30 | ret = (ret & ~96) | (buf.read_uint_bits(4) << 5) 31 | assert ret >= 128 32 | elif val == 96: 33 | ret = (ret & ~96) | (buf.read_uint_bits(7) << 5) 34 | assert ret >= 512 35 | 36 | if ret == 0xfff: 37 | return -1 38 | 39 | return last_index + 1 + ret 40 | 41 | 42 | cpdef list parse_entity_update(Bitbuffer buf, object server_class): 43 | """Parse entity updates. 44 | 45 | First a list of all property indices is generated. For each 46 | property referenced by those indices the property's value is 47 | decoded. The list of properties and their decoded values are 48 | collected and returned. 49 | 50 | :returns: List of updated properties 51 | """ 52 | cdef bint new_way 53 | cdef int val = -1 54 | cdef Decoder decoder 55 | 56 | updated_props = [] 57 | field_indices = [] 58 | 59 | new_way = buf.read_bit() 60 | 61 | while True: 62 | val = read_field_index(buf, val, new_way) 63 | 64 | if val == -1: 65 | break 66 | 67 | field_indices.append(val) 68 | 69 | for index in field_indices: 70 | flattened_prop = server_class['props'][index] 71 | 72 | decoder = Decoder.__new__(Decoder, buf, flattened_prop) 73 | 74 | updated_props.append({ 75 | 'prop': flattened_prop, 76 | 'value': decoder.decode() 77 | }) 78 | 79 | return updated_props 80 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = demoparser 8 | SOURCEDIR = source 9 | BUILDDIR = ../../demoparser-docs 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | html: 20 | @cd ..; python setup.py build_ext --inplace 21 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 22 | 23 | %: Makefile 24 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 25 | -------------------------------------------------------------------------------- /doc/source/api/demoparser.entities.rst: -------------------------------------------------------------------------------- 1 | demoparser\.entities package 2 | ============================ 3 | 4 | Submodules 5 | ---------- 6 | 7 | demoparser\.entities\.base module 8 | --------------------------------- 9 | 10 | .. automodule:: demoparser.entities.base 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | demoparser\.entities\.entity\_list module 16 | ----------------------------------------- 17 | 18 | .. automodule:: demoparser.entities.entity_list 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | demoparser\.entities\.game\_rules module 24 | ---------------------------------------- 25 | 26 | .. automodule:: demoparser.entities.game_rules 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | demoparser\.entities\.player module 32 | ----------------------------------- 33 | 34 | .. automodule:: demoparser.entities.player 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | demoparser\.entities\.team module 40 | --------------------------------- 41 | 42 | .. automodule:: demoparser.entities.team 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | demoparser\.entities\.weapon module 48 | ----------------------------------- 49 | 50 | .. automodule:: demoparser.entities.weapon 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | 56 | Module contents 57 | --------------- 58 | 59 | .. automodule:: demoparser.entities 60 | :members: 61 | :undoc-members: 62 | :show-inheritance: 63 | -------------------------------------------------------------------------------- /doc/source/api/demoparser.rst: -------------------------------------------------------------------------------- 1 | demoparser package 2 | ================== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | demoparser.entities 10 | 11 | Submodules 12 | ---------- 13 | 14 | demoparser\.bitbuffer module 15 | ---------------------------- 16 | 17 | .. automodule:: demoparser.bitbuffer 18 | :members: 19 | :undoc-members: 20 | :show-inheritance: 21 | 22 | demoparser\.bytebuffer module 23 | ----------------------------- 24 | 25 | .. automodule:: demoparser.bytebuffer 26 | :members: 27 | :undoc-members: 28 | :show-inheritance: 29 | 30 | demoparser\.consts module 31 | ------------------------- 32 | 33 | .. automodule:: demoparser.consts 34 | :members: 35 | :undoc-members: 36 | :show-inheritance: 37 | 38 | demoparser\.demofile module 39 | --------------------------- 40 | 41 | .. automodule:: demoparser.demofile 42 | :members: 43 | :undoc-members: 44 | :show-inheritance: 45 | 46 | demoparser\.fields module 47 | ------------------------- 48 | 49 | .. automodule:: demoparser.fields 50 | :members: 51 | :undoc-members: 52 | :show-inheritance: 53 | 54 | demoparser\.props module 55 | ------------------------ 56 | 57 | .. automodule:: demoparser.props 58 | :members: 59 | :undoc-members: 60 | :show-inheritance: 61 | 62 | demoparser\.structures module 63 | ----------------------------- 64 | 65 | .. automodule:: demoparser.structures 66 | :members: 67 | :undoc-members: 68 | :show-inheritance: 69 | 70 | demoparser\.util module 71 | ----------------------- 72 | 73 | .. automodule:: demoparser.util 74 | :members: 75 | :undoc-members: 76 | :show-inheritance: 77 | 78 | 79 | Module contents 80 | --------------- 81 | 82 | .. automodule:: demoparser 83 | :members: 84 | :undoc-members: 85 | :show-inheritance: 86 | -------------------------------------------------------------------------------- /doc/source/api/modules.rst: -------------------------------------------------------------------------------- 1 | demoparser 2 | ========== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | demoparser 8 | -------------------------------------------------------------------------------- /doc/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # demoparser documentation build configuration file, created by 5 | # sphinx-quickstart on Thu Oct 5 16:48:03 2017. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | # 20 | import os 21 | import sys 22 | sys.path.insert(0, os.path.abspath('.')) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | # 28 | # needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = ['sphinx.ext.autodoc', 34 | 'sphinx.ext.doctest', 35 | 'sphinx.ext.coverage', 36 | 'sphinx.ext.viewcode', 37 | 'sphinx.ext.githubpages'] 38 | 39 | # Add any paths that contain templates here, relative to this directory. 40 | templates_path = ['_templates'] 41 | 42 | # The suffix(es) of source filenames. 43 | # You can specify multiple suffix as a list of string: 44 | # 45 | # source_suffix = ['.rst', '.md'] 46 | source_suffix = '.rst' 47 | 48 | # The master toctree document. 49 | master_doc = 'index' 50 | 51 | # General information about the project. 52 | project = 'csgo-demoparser' 53 | copyright = '2017, Ryan Moe' 54 | author = 'Ryan Moe' 55 | 56 | # The version info for the project you're documenting, acts as replacement for 57 | # |version| and |release|, also used in various other places throughout the 58 | # built documents. 59 | # 60 | # The short X.Y version. 61 | version = '0.1.0' 62 | # The full version, including alpha/beta/rc tags. 63 | release = '0.1.0' 64 | 65 | # The language for content autogenerated by Sphinx. Refer to documentation 66 | # for a list of supported languages. 67 | # 68 | # This is also used if you do content translation via gettext catalogs. 69 | # Usually you set "language" from the command line for these cases. 70 | language = None 71 | 72 | # List of patterns, relative to source directory, that match files and 73 | # directories to ignore when looking for source files. 74 | # This patterns also effect to html_static_path and html_extra_path 75 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 76 | 77 | # The name of the Pygments (syntax highlighting) style to use. 78 | pygments_style = 'sphinx' 79 | 80 | # If true, `todo` and `todoList` produce output, else they produce nothing. 81 | todo_include_todos = False 82 | 83 | 84 | # -- Options for HTML output ---------------------------------------------- 85 | 86 | # The theme to use for HTML and HTML Help pages. See the documentation for 87 | # a list of builtin themes. 88 | # 89 | html_theme = 'alabaster' 90 | 91 | # Theme options are theme-specific and customize the look and feel of a theme 92 | # further. For a list of options available for each theme, see the 93 | # documentation. 94 | # 95 | # html_theme_options = {} 96 | 97 | # Add any paths that contain custom static files (such as style sheets) here, 98 | # relative to this directory. They are copied after the builtin static files, 99 | # so a file named "default.css" will overwrite the builtin "default.css". 100 | html_static_path = ['_static'] 101 | 102 | # Custom sidebar templates, must be a dictionary that maps document names 103 | # to template names. 104 | # 105 | # This is required for the alabaster theme 106 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 107 | html_sidebars = { 108 | '**': [ 109 | 'about.html', 110 | 'navigation.html', 111 | 'relations.html', # needs 'show_related': True theme option to display 112 | 'searchbox.html', 113 | 'donate.html', 114 | ] 115 | } 116 | 117 | 118 | # -- Options for HTMLHelp output ------------------------------------------ 119 | 120 | # Output file base name for HTML help builder. 121 | htmlhelp_basename = 'demoparserdoc' 122 | 123 | 124 | # -- Options for LaTeX output --------------------------------------------- 125 | 126 | latex_elements = { 127 | # The paper size ('letterpaper' or 'a4paper'). 128 | # 129 | # 'papersize': 'letterpaper', 130 | 131 | # The font size ('10pt', '11pt' or '12pt'). 132 | # 133 | # 'pointsize': '10pt', 134 | 135 | # Additional stuff for the LaTeX preamble. 136 | # 137 | # 'preamble': '', 138 | 139 | # Latex figure (float) alignment 140 | # 141 | # 'figure_align': 'htbp', 142 | } 143 | 144 | # Grouping the document tree into LaTeX files. List of tuples 145 | # (source start file, target name, title, 146 | # author, documentclass [howto, manual, or own class]). 147 | latex_documents = [ 148 | (master_doc, 'demoparser.tex', 'demoparser Documentation', 149 | 'Ryan Moe', 'manual'), 150 | ] 151 | 152 | 153 | # -- Options for manual page output --------------------------------------- 154 | 155 | # One entry per manual page. List of tuples 156 | # (source start file, name, description, authors, manual section). 157 | man_pages = [ 158 | (master_doc, 'demoparser', 'demoparser Documentation', 159 | [author], 1) 160 | ] 161 | 162 | 163 | # -- Options for Texinfo output ------------------------------------------- 164 | 165 | # Grouping the document tree into Texinfo files. List of tuples 166 | # (source start file, target name, title, author, 167 | # dir menu entry, description, category) 168 | texinfo_documents = [ 169 | (master_doc, 'demoparser', 'demoparser Documentation', 170 | author, 'demoparser', 'One line description of project.', 171 | 'Miscellaneous'), 172 | ] 173 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /doc/source/demopacket.rst: -------------------------------------------------------------------------------- 1 | - net_NOP 2 | - net_Disconnect 3 | - net_File 4 | - net_SplitScreenUser 5 | - net_Tick 6 | - net_StringCmd 7 | - net_SetConVar 8 | - net_SignonState 9 | - net_PlayerAvatarData 10 | - svc_ServerInfo 11 | - svc_SendTable 12 | - svc_ClassInfo 13 | - svc_SetPause 14 | - svc_CreateStringTable 15 | - svc_UpdateStringTable 16 | - svc_VoiceInit 17 | - svc_VoiceData 18 | - svc_Print 19 | - svc_Sounds 20 | - svc_SetView 21 | - svc_FixAngle 22 | - svc_CrosshairAngle 23 | - svc_BSPDecal 24 | - svc_SplitScreen 25 | - svc_UserMessage 26 | - svc_EntityMessage 27 | - svc_GameEvent 28 | - svc_PacketEntities 29 | - svc_TempEntities 30 | - svc_Prefetch 31 | - svc_Menu 32 | - svc_GameEventList 33 | - svc_GetCvarValue 34 | - svc_PaintmapData 35 | - svc_CmdKeyValues 36 | - svc_EncryptedData 37 | - svc_HltvReplay 38 | - svc_Broadcast_Command 39 | -------------------------------------------------------------------------------- /doc/source/events.rst: -------------------------------------------------------------------------------- 1 | Events 2 | ------ 3 | 4 | As demo files are parsed events are emitted. Callbacks can be 5 | registered for each event. The arguments passed to the callbacks 6 | are described for each event below. 7 | 8 | ----- 9 | 10 | .. _event_baseline_create: 11 | 12 | ``baseline_create`` 13 | An instance baseline has been created. 14 | 15 | Callback arguments: 16 | :class_id: Server class ID. 17 | :table: Data table for this server class. 18 | :baseline: Instance baseline object. 19 | 20 | .. _event_change: 21 | 22 | ``change`` 23 | An entity's property has been changed. 24 | 25 | Callback arguments: 26 | :entity: Entity being created or updated. 27 | :table_name: Table which contains the property being updated. 28 | :var_name: Name of property being updated. 29 | :value: New value of property. 30 | 31 | .. _event_datatable_ready: 32 | 33 | ``datatable_ready`` 34 | Data table has been created and all pending baselines have been handled. 35 | 36 | Callback arguments: 37 | :table: Data table instance 38 | 39 | .. _event_demo_packet: 40 | 41 | ``demo_packet`` 42 | An event for each type of demo packet will be emitted. Demo packets 43 | are instances of SVC and NET classes. 44 | 45 | Callback arguments: 46 | :class_name: Name of packet. 47 | :packet: Instance of approprite NET\_ or SVC\_ class. 48 | 49 | 50 | The following is a list of all demo packet types: 51 | 52 | .. include:: demopacket.rst 53 | 54 | 55 | .. _event_end: 56 | 57 | ``end`` 58 | Processing has finished. 59 | 60 | 61 | .. _event_game_event: 62 | 63 | ``game_event`` 64 | An event for the specific type of game event will be emitted. 65 | 66 | Callback arguments: 67 | :event: Game event object. 68 | :msg: The original message of type SVC_GameEvent which triggered 69 | this event. 70 | 71 | The following is a list of game events for CS:GO: 72 | 73 | .. include:: game_events.rst 74 | 75 | .. _event_string_table_update: 76 | 77 | ``string_table_update`` 78 | An entry in a string table has been updated. 79 | 80 | Callback arguments: 81 | :table: String table instance. 82 | :index: Index of entry being updated. 83 | :entry: The updated entry. 84 | :user_data: User data for the updated entry. 85 | 86 | .. _event_tick_start: 87 | 88 | ``tick_start`` 89 | Start of new game tick. 90 | 91 | Callback arguments: 92 | :current_tick: Tick which has just started. 93 | 94 | .. _event_tick_end: 95 | 96 | ``tick_end`` 97 | End of game tick. 98 | 99 | Callback arguments: 100 | :current_tick: Tick which has just ended. 101 | 102 | .. _event_user_msg: 103 | 104 | ``user_message`` 105 | An event for the specific type of user message will be emitted for each 106 | user message command. A callback can be added for each specific type of 107 | user message. 108 | 109 | .. code-block:: python 110 | 111 | def um_chat(msg_type, msg): 112 | assert msg_type == 'SayText2' 113 | 114 | d = demofile(...) 115 | d.add_callback('SayText2', um_chat) 116 | 117 | 118 | Callback arguments: 119 | :message_type: One of types listed below. 120 | :message: Instance of the specied user message class. 121 | 122 | 123 | The following is a list of all user message types: 124 | 125 | .. include:: user_messages.rst 126 | -------------------------------------------------------------------------------- /doc/source/game_events.rst: -------------------------------------------------------------------------------- 1 | - achievement_earned 2 | - achievement_earned_local 3 | - achievement_event 4 | - achievement_increment 5 | - achievement_info_loaded 6 | - achievement_write_failed 7 | - add_bullet_hit_marker 8 | - add_player_sonar_icon 9 | - ammo_pickup 10 | - announce_phase_end 11 | - assassination_target_killed 12 | - begin_new_match 13 | - bomb_abortdefuse 14 | - bomb_abortplant 15 | - bomb_beep 16 | - bomb_begindefuse 17 | - bomb_beginplant 18 | - bomb_defused 19 | - bomb_dropped 20 | - bomb_exploded 21 | - bomb_pickup 22 | - bomb_planted 23 | - bonus_updated 24 | - bot_takeover 25 | - break_breakable 26 | - break_prop 27 | - bullet_impact 28 | - buymenu_close 29 | - buymenu_open 30 | - buytime_ended 31 | - cart_updated 32 | - client_disconnect 33 | - client_loadout_changed 34 | - cs_game_disconnected 35 | - cs_intermission 36 | - cs_match_end_restart 37 | - cs_pre_restart 38 | - cs_prev_next_spectator 39 | - cs_round_final_beep 40 | - cs_round_start_beep 41 | - cs_win_panel_match 42 | - cs_win_panel_round 43 | - decoy_detonate 44 | - decoy_firing 45 | - decoy_started 46 | - defuser_dropped 47 | - defuser_pickup 48 | - difficulty_changed 49 | - dm_bonus_weapon_start 50 | - door_moving 51 | - enable_restart_voting 52 | - endmatch_cmm_start_reveal_items 53 | - endmatch_mapvote_selecting_map 54 | - enter_bombzone 55 | - enter_buyzone 56 | - enter_rescue_zone 57 | - entity_killed 58 | - entity_visible 59 | - exit_bombzone 60 | - exit_buyzone 61 | - exit_rescue_zone 62 | - finale_start 63 | - flare_ignite_npc 64 | - flashbang_detonate 65 | - freezecam_started 66 | - game_end 67 | - game_init 68 | - gameinstructor_draw 69 | - gameinstructor_nodraw 70 | - game_message 71 | - game_newmap 72 | - game_start 73 | - gameui_hidden 74 | - gc_connected 75 | - gg_bonus_grenade_achieved 76 | - gg_final_weapon_achieved 77 | - gg_killed_enemy 78 | - gg_leader 79 | - gg_player_impending_upgrade 80 | - gg_player_levelup 81 | - ggprogressive_player_levelup 82 | - gg_reset_round_start_sounds 83 | - gg_team_leader 84 | - ggtr_player_levelup 85 | - grenade_bounce 86 | - grenade_thrown 87 | - hegrenade_detonate 88 | - helicopter_grenade_punt_miss 89 | - hide_freezepanel 90 | - hltv_cameraman 91 | - hltv_changed_mode 92 | - hltv_changed_target 93 | - hltv_chase 94 | - hltv_chat 95 | - hltv_fixed 96 | - hltv_message 97 | - hltv_rank_camera 98 | - hltv_rank_entity 99 | - hltv_status 100 | - hltv_title 101 | - hostage_call_for_help 102 | - hostage_follows 103 | - hostage_hurt 104 | - hostage_killed 105 | - hostage_rescued 106 | - hostage_rescued_all 107 | - hostage_stops_following 108 | - hostname_changed 109 | - inferno_expire 110 | - inferno_extinguish 111 | - inferno_startburn 112 | - inspect_weapon 113 | - instructor_server_hint_create 114 | - instructor_server_hint_stop 115 | - inventory_updated 116 | - item_equip 117 | - item_found 118 | - item_pickup 119 | - item_purchase 120 | - item_remove 121 | - item_schema_initialized 122 | - items_gifted 123 | - jointeam_failed 124 | - map_transition 125 | - match_end_conditions 126 | - material_default_complete 127 | - mb_input_lock_cancel 128 | - mb_input_lock_success 129 | - molotov_detonate 130 | - nav_blocked 131 | - nav_generate 132 | - nextlevel_changed 133 | - other_death 134 | - physgun_pickup 135 | - player_activate 136 | - player_avenged_teammate 137 | - player_blind 138 | - player_changename 139 | - player_chat 140 | - player_class 141 | - player_connect 142 | - player_connect_full 143 | - player_death 144 | - player_decal 145 | - player_disconnect 146 | - player_falldamage 147 | - player_footstep 148 | - player_given_c4 149 | - player_hintmessage 150 | - player_hurt 151 | - player_info 152 | - player_jump 153 | - player_radio 154 | - player_reset_vote 155 | - player_say 156 | - player_score 157 | - player_shoot 158 | - player_spawn 159 | - player_spawned 160 | - player_stats_updated 161 | - player_team 162 | - player_use 163 | - ragdoll_dissolved 164 | - read_game_titledata 165 | - repost_xbox_achievements 166 | - reset_game_titledata 167 | - reset_player_controls 168 | - round_announce_final 169 | - round_announce_last_round_half 170 | - round_announce_match_point 171 | - round_announce_match_start 172 | - round_announce_warmup 173 | - round_end 174 | - round_end_upload_stats 175 | - round_freeze_end 176 | - round_mvp 177 | - round_officially_ended 178 | - round_poststart 179 | - round_prestart 180 | - round_start 181 | - round_start_pre_entity 182 | - round_time_warning 183 | - seasoncoin_levelup 184 | - server_addban 185 | - server_cvar 186 | - server_message 187 | - server_pre_shutdown 188 | - server_removeban 189 | - server_shutdown 190 | - server_spawn 191 | - set_instructor_group_enabled 192 | - sfuievent 193 | - show_freezepanel 194 | - silencer_detach 195 | - silencer_off 196 | - silencer_on 197 | - smokegrenade_detonate 198 | - smokegrenade_expired 199 | - spec_mode_updated 200 | - spec_target_updated 201 | - start_halftime 202 | - start_vote 203 | - store_pricesheet_updated 204 | - survival_announce_phase 205 | - switch_team 206 | - tagrenade_detonate 207 | - teamchange_pending 208 | - team_info 209 | - teamplay_broadcast_audio 210 | - teamplay_round_start 211 | - team_score 212 | - tournament_reward 213 | - tr_exit_hint_trigger 214 | - trial_time_expired 215 | - tr_mark_best_time 216 | - tr_mark_complete 217 | - tr_player_flashbanged 218 | - tr_show_exit_msgbox 219 | - tr_show_finish_msgbox 220 | - ugc_file_download_finished 221 | - ugc_file_download_start 222 | - ugc_map_download_error 223 | - ugc_map_info_received 224 | - ugc_map_unsubscribed 225 | - update_matchmaking_stats 226 | - user_data_downloaded 227 | - verify_client_hit 228 | - vip_escaped 229 | - vip_killed 230 | - vote_cast 231 | - vote_changed 232 | - vote_ended 233 | - vote_failed 234 | - vote_options 235 | - vote_passed 236 | - vote_started 237 | - weapon_fire 238 | - weapon_fire_on_empty 239 | - weapon_outofammo 240 | - weapon_reload 241 | - weapon_zoom 242 | - weapon_zoom_rifle 243 | - write_game_titledata 244 | - write_profile_data 245 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | .. demoparser documentation master file, created by 2 | sphinx-quickstart on Thu Oct 5 16:48:03 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to demoparser's documentation! 7 | ====================================== 8 | 9 | .. include:: ../../README.rst 10 | 11 | Contents: 12 | 13 | .. toctree:: 14 | :maxdepth: 1 15 | 16 | usage 17 | events 18 | API Documentation 19 | 20 | -------------------------------------------------------------------------------- /doc/source/intro.rst: -------------------------------------------------------------------------------- 1 | Demoparser is a library for parsing .DEM files generated by CS:GO. 2 | 3 | As the file is processed events are emitted for which callbacks can 4 | be registered. 5 | -------------------------------------------------------------------------------- /doc/source/usage.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | Detailed Usage 3 | ============== 4 | 5 | This example shows how you can collect information from a demo file. In 6 | this case it's gathering data about every player death. 7 | 8 | In order to do this you need to know which event to add a callback for. 9 | The first step is to check the list of `game events`_. From the above documentation you 10 | can see the structure of the ``player_death`` event. 11 | 12 | You need an instance of :py:class:`demoparser.demofile.DemoFile` to parse 13 | the demo:: 14 | 15 | from demoparser.demofile import DemoFile 16 | 17 | and a callback function for the ``player_death`` event: 18 | 19 | .. literalinclude:: ../../demoparser/examples/player_death.py 20 | :lines: 6-30 21 | 22 | 23 | .. note:: 24 | The DemoFile instance should generally be in-scope for callback functions. 25 | Often a callback will need to refer to the list of active entities or 26 | other parser state. 27 | 28 | 29 | Retrieving the event data by name requires an extra step and some background 30 | information. 31 | 32 | At the beginning of the demo a message containing all game events is 33 | sent (svc_GameEventList). Each item in the list contains an event ID and 34 | a list of keys with their type and name. 35 | 36 | When an event occurs during the game the message from the server only contains 37 | the type and value. To understand what those values refer to you consult 38 | the game event from the list of events (this event is the first argument 39 | to the callback). The list of keys from the server message and the list of 40 | keys in the game event are in the same order. 41 | 42 | Here's a portion of what's in the message passed to the callback:: 43 | 44 | eventid: 23 45 | keys { 46 | type: 4 47 | val_short: 5 48 | } 49 | keys { 50 | type: 4 51 | val_short: 28 52 | } 53 | keys { 54 | type: 4 55 | val_short: 6 56 | } 57 | keys { 58 | type: 1 59 | val_string: "knife_default_ct" 60 | } 61 | keys { 62 | type: 6 63 | val_bool: false 64 | } 65 | 66 | and here's what's in the event:: 67 | 68 | eventid: 23 69 | name: "player_death" 70 | keys { 71 | type: 4 72 | name: "userid" 73 | } 74 | keys { 75 | type: 4 76 | name: "attacker" 77 | } 78 | keys { 79 | type: 4 80 | name: "assister" 81 | } 82 | keys { 83 | type: 1 84 | name: "weapon" 85 | } 86 | keys { 87 | type: 6 88 | name: "headshot" 89 | } 90 | 91 | 92 | Therefore, the value of ``userid`` is ``msg.keys[0].val_short`` because 93 | ``userid`` is the zeroth element in the list of game event keys. 94 | 95 | 96 | Now that a callback is defined the demo file can be parsed. 97 | 98 | .. literalinclude:: ../../demoparser/examples/player_death.py 99 | :lines: 33-37 100 | 101 | 102 | Output:: 103 | 104 | --- Player Death at tick 4156--- 105 | b0RUP killed by duMzyy * ringwald with knife_default_ct. Headshot? No. 106 | Attacker: health = 100 position = {'x': -420.1773681640625, 'y': -186.3490753173828, 'z': -215.97097778320312} 107 | Victim: position = {'x': -454.6003112792969, 'y': -176.74295043945312, 'z': -219.0527801513672} 108 | 109 | --- Player Death at tick 4200--- 110 | duMzyy * ringwald killed by smF with knife_t. Headshot? No. 111 | Attacker: health = 100 position = {'x': -454.4964599609375, 'y': -157.48977661132812, 'z': -219.96875} 112 | Victim: position = {'x': -451.5458679199219, 'y': -189.49244689941406, 'z': -215.96875} 113 | 114 | 115 | .. _game events: https://wiki.alliedmods.net/Counter-Strike:_Global_Offensive_Events 116 | -------------------------------------------------------------------------------- /doc/source/user_messages.rst: -------------------------------------------------------------------------------- 1 | - VGUIMenu 2 | - Geiger 3 | - Train 4 | - HudText 5 | - SayText 6 | - SayText2 7 | - TextMsg 8 | - HudMsg 9 | - ResetHud 10 | - GameTitle 11 | - Shake 12 | - Fade 13 | - Rumble 14 | - CloseCaption 15 | - CloseCaptionDirect 16 | - SendAudio 17 | - RawAudio 18 | - VoiceMask 19 | - RequestState 20 | - Damage 21 | - RadioText 22 | - HintText 23 | - KeyHintText 24 | - ProcessSpottedEntityUpdate 25 | - ReloadEffect 26 | - AdjustMoney 27 | - UpdateTeamMoney 28 | - StopSpectatorMode 29 | - KillCam 30 | - DesiredTimescale 31 | - CurrentTimescale 32 | - AchievementEvent 33 | - MatchEndConditions 34 | - DisconnectToLobby 35 | - PlayerStatsUpdate 36 | - DisplayInventory 37 | - WarmupHasEnded 38 | - ClientInfo 39 | - XRankGet 40 | - XRankUpd 41 | - CallVoteFailed 42 | - VoteStart 43 | - VotePass 44 | - VoteFailed 45 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | suitcase 2 | cython 3 | protobuf 4 | sphinx 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = csgo-demoparser 3 | author = Ryan Moe 4 | author-email = ryan.moe@gmail.com 5 | summary = CS:GO demo file parser. 6 | description-file = README.rst 7 | home-page = https://github.com/ibm-dev-incubator/demoparser 8 | license = Apache-2 9 | license_file = LICENSE 10 | classifier = 11 | Development Status :: 4 - Beta 12 | License :: OSI Approved :: Apache Software License 13 | Operating System :: OS Independent 14 | Programming Language :: Python 15 | keywords = 16 | setup 17 | distutils 18 | 19 | [global] 20 | commands = demoparser._setup_hooks.ProtobufBuilder 21 | 22 | [files] 23 | packages = 24 | demoparser 25 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | from setuptools.extension import Extension 5 | 6 | from Cython.Build import cythonize 7 | 8 | 9 | extensions = [ 10 | Extension( 11 | "demoparser.util", 12 | ["demoparser/util.pyx"] 13 | ), 14 | Extension( 15 | "demoparser.props", 16 | ["demoparser/props.pyx"], 17 | ), 18 | Extension( 19 | "demoparser.bitbuffer", 20 | ["demoparser/bitbuffer.pyx"] 21 | ), 22 | Extension( 23 | "demoparser.demofile", 24 | ["demoparser/demofile.pyx"] 25 | ), 26 | ] 27 | 28 | setup( 29 | setup_requires=['pbr>=1.9', 'setuptools>=17.1'], 30 | pbr=True, 31 | ext_modules=cythonize( 32 | extensions, compiler_directives={ 33 | "embedsignature": True, 34 | "linetrace": True, 35 | "language_level": 3 36 | } 37 | ) 38 | ) 39 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | flake8 2 | pytest 3 | -------------------------------------------------------------------------------- /tests/test_bitbuffer.py: -------------------------------------------------------------------------------- 1 | from demoparser.bitbuffer import Bitbuffer 2 | 3 | 4 | def test_read_bit(): 5 | b = Bitbuffer(b'\xff\xff\xff\xff\x00') 6 | 7 | # Bitbuffer reads 32-bits at a time so 32 bits must be 8 | # read to check that it moves to the next word correctly. 9 | bits = [b.read_bit() for i in range(32)] 10 | assert bits == [1] * 32 11 | 12 | # Next word is 0 so next bit should be 0 13 | assert b.read_bit() == 0 14 | 15 | 16 | def test_read_uint_bits(): 17 | # Buffer is shown below and the word boundary is marked by ||. 18 | # 00100001000111111111000000000011 || 00110010 19 | b = Bitbuffer(b'\x00\xff\x11\x22\x33') 20 | 21 | # Read 0010000100011111111100000000 22 | num = b.read_uint_bits(28) 23 | assert num == 34733824 24 | 25 | # Test reading integers across word bundary 26 | # This will read the final 0011 from the first word 27 | # followed by 00110010 for a total of 12 bits. This value 28 | # should equal 001100110010. 29 | num = b.read_uint_bits(12) 30 | assert num == 818 31 | 32 | 33 | def test_read_sint_bits(): 34 | # 10000010 35 | b = Bitbuffer(b'\x82') 36 | num = b.read_sint_bits(8) 37 | assert num == -126 38 | 39 | # Unsigned this time 40 | # 00000010 41 | b = Bitbuffer(b'\x02') 42 | num = b.read_sint_bits(8) 43 | assert num == 2 44 | 45 | 46 | def test_read_string(): 47 | 48 | # Read until \0 49 | b = Bitbuffer(b'test\x00more\xff') 50 | assert b.read_string() == 'test' 51 | 52 | # Next value will be 'm' 53 | assert chr(b.read_uint_bits(8)) == 'm' 54 | 55 | # Read a fixed length 56 | # \x00more has been read but not returned 57 | b = Bitbuffer(b'test\x00more\xff') 58 | assert b.read_string(9) == 'test' 59 | 60 | # All that sould be left is \xff which is 255 61 | assert b.read_uint_bits(8) == 255 62 | -------------------------------------------------------------------------------- /tests/test_bytebuffer.py: -------------------------------------------------------------------------------- 1 | from demoparser.bytebuffer import Bytebuffer 2 | from demoparser import structures 3 | 4 | 5 | def test_read(): 6 | b = Bytebuffer(b'\x01\x02\x03') 7 | assert b.read(3) == b'\x01\x02\x03' 8 | 9 | 10 | def test_read_command_header(): 11 | b = Bytebuffer(b'\x01\x02\x03\x04\x05\x06') 12 | header = b.read_command_header() 13 | 14 | assert type(header) == structures.CommandHeader 15 | assert header.command == 1 16 | assert header.tick == 84148994 17 | assert header.player == 6 18 | -------------------------------------------------------------------------------- /tests/test_demofile.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from demoparser.demofile import CommandError 4 | from demoparser.demofile import DemoFile 5 | from demoparser.structures import CommandHeader 6 | 7 | 8 | def test_parse_invalid_command(): 9 | # Demo files have a 1072 byte header that isn't needed here 10 | data = b'HL2DEMO\x00' + bytes([0] * 1064) 11 | header = CommandHeader() 12 | header.player = 1 13 | header.tick = 1 14 | header.command = 99 15 | 16 | df = DemoFile(data + header.pack()) 17 | with pytest.raises(CommandError) as exc: 18 | df.parse() 19 | 20 | assert 'Unrecognized command' in str(exc.value) 21 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py35,py36,lint 3 | 4 | [testenv] 5 | deps= 6 | -rtest-requirements.txt 7 | -rrequirements.txt 8 | commands= 9 | python setup.py build_ext --force --inplace --define CYTHON_TRACE 10 | py.test 11 | 12 | [testenv:lint] 13 | basepython = python3 14 | commands = 15 | flake8 16 | flake8 --filename *.pyx --ignore E999,E225,E226,E201,E202,E227 17 | 18 | [flake8] 19 | ignore = D203 20 | exclude = 21 | .tox, 22 | .git, 23 | __pycache__, 24 | doc/source/conf.py, 25 | old, 26 | build, 27 | dist, 28 | protobufs 29 | .eggs 30 | --------------------------------------------------------------------------------