overlook-csgo 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +2 -0
  3. data/.gitignore +5 -0
  4. data/.rspec +2 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +115 -0
  8. data/README.md +77 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +7 -0
  11. data/bin/setup +7 -0
  12. data/lib/overlook.rb +20 -0
  13. data/lib/overlook/bit_buffer.rb +52 -0
  14. data/lib/overlook/bit_reader.rb +64 -0
  15. data/lib/overlook/byte_reader.rb +58 -0
  16. data/lib/overlook/csgo.rb +9 -0
  17. data/lib/overlook/csgo/demo.rb +33 -0
  18. data/lib/overlook/csgo/demo/base.rb +20 -0
  19. data/lib/overlook/csgo/demo/command.rb +67 -0
  20. data/lib/overlook/csgo/demo/command_factory.rb +15 -0
  21. data/lib/overlook/csgo/demo/encrypted_data_handler.rb +16 -0
  22. data/lib/overlook/csgo/demo/game_event_list_message_handler.rb +19 -0
  23. data/lib/overlook/csgo/demo/game_event_message_handler.rb +35 -0
  24. data/lib/overlook/csgo/demo/header.rb +45 -0
  25. data/lib/overlook/csgo/demo/key_reader.rb +18 -0
  26. data/lib/overlook/csgo/demo/packet.rb +16 -0
  27. data/lib/overlook/csgo/demo/packet_factory.rb +22 -0
  28. data/lib/overlook/csgo/demo/packet_handler.rb +50 -0
  29. data/lib/overlook/csgo/demo/parser.rb +66 -0
  30. data/lib/overlook/csgo/demo/user_message_message_handler.rb +43 -0
  31. data/lib/overlook/csgo/match_info_decoder.rb +92 -0
  32. data/lib/overlook/csgo/sharecode_decoder.rb +83 -0
  33. data/lib/overlook/version.rb +3 -0
  34. data/lib/proto/.gitkeep +0 -0
  35. data/lib/proto/compiled/cstrike15_gcmessages.pb.rb +1382 -0
  36. data/lib/proto/compiled/cstrike15_usermessages.pb.rb +702 -0
  37. data/lib/proto/compiled/descriptor.pb.rb +280 -0
  38. data/lib/proto/compiled/netmessages.pb.rb +687 -0
  39. data/lib/proto/compiled/steammessages.pb.rb +798 -0
  40. data/overlook-csgo.gemspec +36 -0
  41. metadata +209 -0
@@ -0,0 +1,9 @@
1
+ require 'overlook/csgo/sharecode_decoder'
2
+ require 'overlook/csgo/match_info_decoder'
3
+
4
+ require 'overlook/csgo/demo'
5
+
6
+ module Overlook
7
+ module Csgo
8
+ end
9
+ end
@@ -0,0 +1,33 @@
1
+ require 'overlook/csgo/demo/base'
2
+
3
+ require 'overlook/csgo/demo/packet'
4
+ require 'overlook/csgo/demo/command'
5
+ require 'overlook/csgo/demo/header'
6
+
7
+ require 'overlook/csgo/demo/parser'
8
+
9
+ require 'overlook/csgo/demo/key_reader'
10
+
11
+ require 'overlook/csgo/demo/packet_handler'
12
+
13
+ require 'overlook/csgo/demo/user_message_message_handler'
14
+ require 'overlook/csgo/demo/game_event_list_message_handler'
15
+ require 'overlook/csgo/demo/game_event_message_handler'
16
+ require 'overlook/csgo/demo/encrypted_data_handler'
17
+
18
+ require 'overlook/csgo/demo/command_factory'
19
+ require 'overlook/csgo/demo/packet_factory'
20
+
21
+ module Overlook
22
+ module Csgo
23
+ module Demo
24
+ class << self
25
+ def open(io)
26
+ Base.new(io)
27
+ end
28
+
29
+ alias :new :open
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+
2
+ module Overlook
3
+ module Csgo
4
+ module Demo
5
+ class Base
6
+ def initialize(io)
7
+ @parser = Parser.new(io)
8
+ end
9
+
10
+ def subscribe(subscriber)
11
+ @parser.subscribe(subscriber)
12
+ end
13
+
14
+ def parse
15
+ @parser.parse
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,67 @@
1
+
2
+
3
+ module Overlook
4
+ module Csgo
5
+ module Demo
6
+ class Command
7
+
8
+ module TYPE
9
+ SIGNON = 1
10
+ PACKET = 2
11
+ SYNCTICK = 3
12
+ CONSOLECMD = 4
13
+ USERCMD = 5
14
+ DATATABLES = 6
15
+ STOP = 7
16
+ CUSTOMDATA = 8
17
+ STRINGTABLES = 9
18
+ LASTCMD = STRINGTABLES
19
+ end
20
+
21
+ attr_accessor :type, :tick, :slot
22
+
23
+ def initialize(type, tick, slot)
24
+ @type = type
25
+ @tick = tick
26
+ @slot = slot
27
+ @tick = 0 unless packet?
28
+ end
29
+
30
+ def type
31
+ case @type
32
+ when Command::TYPE::PACKET, Command::TYPE::SIGNON
33
+ :packet
34
+ when Command::TYPE::CUSTOMDATA, Command::TYPE::STRINGTABLES, Command::TYPE::DATATABLES
35
+ :skipable
36
+ when Command::TYPE::SYNCTICK
37
+ :sync
38
+ when Command::TYPE::STOP
39
+ :stop
40
+ else
41
+ :unknown
42
+ end
43
+ end
44
+
45
+ def unknown?
46
+ type == :stop
47
+ end
48
+
49
+ def stop?
50
+ type == :stop
51
+ end
52
+
53
+ def sync?
54
+ type == :sync
55
+ end
56
+
57
+ def packet?
58
+ type == :packet
59
+ end
60
+
61
+ def skipable?
62
+ type == :skipable
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,15 @@
1
+ module Overlook
2
+ module Csgo
3
+ module Demo
4
+ class CommandFactory
5
+ def self.from_io(reader)
6
+ cmd = reader.byte
7
+ tick = reader.signed_int32
8
+ slot = reader.byte
9
+
10
+ Command.new(cmd, tick, slot)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module Overlook
2
+ module Csgo
3
+ module Demo
4
+ class EncryptedDataHandler
5
+ def initialize(parser)
6
+ @parser = parser
7
+ end
8
+
9
+ def handle(message)
10
+ # nop
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,19 @@
1
+ module Overlook
2
+ module Csgo
3
+ module Demo
4
+ class GameEventListMessageHandler
5
+ def initialize(parser)
6
+ @parser = parser
7
+ end
8
+
9
+ def handle(message)
10
+ game_event_list_message = CSVCMsg_GameEventList.decode(message)
11
+
12
+ game_event_list_message.descriptors.each do |descriptor|
13
+ @parser.descriptors[descriptor.eventid] ||= descriptor
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ module Overlook
2
+ module Csgo
3
+ module Demo
4
+ class GameEventMessageHandler
5
+ def initialize(parser)
6
+ @parser = parser
7
+ end
8
+
9
+ def handle(message)
10
+ game_event_message = CSVCMsg_GameEvent.decode(message)
11
+ descriptor = @parser.descriptors[game_event_message.eventid]
12
+ event_keys = game_event_message.keys || []
13
+
14
+ keys = descriptor.keys
15
+ key_reader = KeyReader.new
16
+ attributes = event_keys.each_with_index.inject({}) do |memo, (key, index)|
17
+ memo[keys[index].name] = key_reader.read(key)
18
+ memo
19
+ end
20
+
21
+ case descriptor.name
22
+ when 'player_connect', 'player_connect_full'
23
+ # @parser.emit ...
24
+ when 'player_hurt'
25
+ when 'weapon_fire'
26
+ when 'bomb_beginplant', 'bomb_abortplant', 'bomb_planted', 'bomb_defused', 'bomb_exploded'
27
+ when 'round_end'
28
+ when 'begin_new_match'
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,45 @@
1
+
2
+ module Overlook
3
+ module Csgo
4
+ module Demo
5
+ class Header
6
+ MAX_STRING_LENGTH = 260.freeze
7
+
8
+ attr_accessor :map_name
9
+
10
+ def initialize(map_name)
11
+ raise ArgumentError 'map_name can\'t be nil' if map_name.nil?
12
+
13
+ @map_name = map_name
14
+ end
15
+
16
+ def to_hash
17
+ { map_name: map_name }
18
+ end
19
+
20
+ def map_name
21
+ @map_name.split("\x00").first
22
+ end
23
+
24
+ def self.from_io(reader)
25
+ stamp = reader.read(8)
26
+ demo_protocol = reader.signed_int32
27
+ network_protocol = reader.signed_int32
28
+ server_name = reader.string(MAX_STRING_LENGTH)
29
+ client_name = reader.string(MAX_STRING_LENGTH)
30
+ map_name = reader.string(MAX_STRING_LENGTH)
31
+ game_directory = reader.string(MAX_STRING_LENGTH)
32
+ playtime = reader.float
33
+ ticks = reader.signed_int32
34
+ frames = reader.signed_int32
35
+ signon_length = reader.signed_int32
36
+
37
+ raise InvalidDemo, "#{self.class.name} only supports HL2DEMO, got #{stamp}" if stamp !~ /hl2demo/i
38
+ raise InvalidDemo, "#{self.class.name} only supports Valve demo files." if server_name !~ /valve/i
39
+
40
+ new(map_name)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,18 @@
1
+ module Overlook
2
+ module Csgo
3
+ module Demo
4
+ class KeyReader
5
+ def read(key)
6
+ {
7
+ 1 => key.val_string,
8
+ 2 => key.val_float,
9
+ 3 => key.val_long,
10
+ 4 => key.val_short,
11
+ 5 => key.val_byte,
12
+ 6 => key.val_bool
13
+ }[key.type]
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+
2
+ module Overlook
3
+ module Csgo
4
+ module Demo
5
+ class Packet
6
+ attr_accessor :size, :data, :command_info
7
+
8
+ def initialize(size, data, command_info)
9
+ @size = size
10
+ @data = data
11
+ @command_info = command_info
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ module Overlook
2
+ module Csgo
3
+ module Demo
4
+ # Allows creation of Packet objects
5
+ class PacketFactory
6
+ COMMAND_INFO_SIZE = 160.freeze
7
+
8
+ # Creates a packet from an io stream.
9
+ #
10
+ # @return [Packet] The created packet object
11
+ def self.from_io(io)
12
+ command_info = io.read(COMMAND_INFO_SIZE)
13
+ size = io.signed_int32
14
+ data = StringIO.new(io.read(size))
15
+
16
+ Packet.new(size,
17
+ data, command_info)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,50 @@
1
+ module Overlook
2
+ module Csgo
3
+ module Demo
4
+ # Determines which handler to use to read a packet and yields that
5
+ # handler along with the message to the block
6
+ class PacketHandler
7
+ module MessageTypes
8
+ USER_MESSAGE_MESSAGE_ID = 23
9
+ GAME_EVENT_MESSAGE_ID = 25
10
+ GAME_EVENT_LIST_MESSAGE_ID = 30
11
+ ENCRYPTED_DATA_ID = 35
12
+ end
13
+
14
+ attr_reader :parser
15
+
16
+ def initialize(_parser)
17
+ @parser = _parser
18
+ end
19
+
20
+ def handle(packet)
21
+ reader = BitReader.new(packet.data)
22
+
23
+ loop do
24
+ message_type = reader.var_int32
25
+ message_size = reader.var_int32
26
+
27
+ if handler = handlers[message_type]
28
+ handler.handle(reader.bytes(message_size))
29
+ else
30
+ reader.skip(message_size)
31
+ end
32
+
33
+ break if reader.eof?
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def handlers
40
+ {
41
+ MessageTypes::USER_MESSAGE_MESSAGE_ID => UserMessageMessageHandler.new(parser),
42
+ MessageTypes::GAME_EVENT_LIST_MESSAGE_ID => GameEventListMessageHandler.new(parser),
43
+ MessageTypes::GAME_EVENT_MESSAGE_ID => GameEventMessageHandler.new(parser),
44
+ MessageTypes::ENCRYPTED_DATA_ID => EncryptedDataHandler.new(parser),
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,66 @@
1
+ require 'wisper'
2
+
3
+ module Overlook
4
+ module Csgo
5
+ module Demo
6
+ class Parser
7
+ include Wisper::Publisher
8
+
9
+ Exception = Class.new(StandardError)
10
+ InvalidDemo = Class.new(Exception)
11
+
12
+ attr_reader :reader
13
+ attr_accessor :descriptors
14
+
15
+ def initialize(io)
16
+ @reader = ByteReader.new(io)
17
+ @header = nil
18
+ @descriptors = {}
19
+ end
20
+
21
+ def parse
22
+ header
23
+ packets
24
+ done
25
+ end
26
+
27
+ def emit(event, data = {})
28
+ broadcast(event.to_sym, data)
29
+ end
30
+
31
+ private
32
+
33
+ def header
34
+ emit(:header,
35
+ Header.from_io(reader).to_hash)
36
+ end
37
+
38
+ def done
39
+ emit(:done)
40
+ true
41
+ end
42
+
43
+ def packets
44
+ packet_handler = PacketHandler.new(self)
45
+
46
+ loop do
47
+ command = CommandFactory.from_io(reader)
48
+
49
+ next if command.sync?
50
+ break if command.stop?
51
+ raise "Unknown Command: #{command.type}" if command.unknown?
52
+
53
+ if command.packet?
54
+ packet = PacketFactory.from_io(reader)
55
+ packet_handler.handle(packet)
56
+ elsif command.skipable?
57
+ reader.read(reader.signed_int32)
58
+ else
59
+ raise "Unknown Command #{command.type}"
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end