overlook-csgo 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitattributes +2 -0
- data/.gitignore +5 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +115 -0
- data/README.md +77 -0
- data/Rakefile +6 -0
- data/bin/console +7 -0
- data/bin/setup +7 -0
- data/lib/overlook.rb +20 -0
- data/lib/overlook/bit_buffer.rb +52 -0
- data/lib/overlook/bit_reader.rb +64 -0
- data/lib/overlook/byte_reader.rb +58 -0
- data/lib/overlook/csgo.rb +9 -0
- data/lib/overlook/csgo/demo.rb +33 -0
- data/lib/overlook/csgo/demo/base.rb +20 -0
- data/lib/overlook/csgo/demo/command.rb +67 -0
- data/lib/overlook/csgo/demo/command_factory.rb +15 -0
- data/lib/overlook/csgo/demo/encrypted_data_handler.rb +16 -0
- data/lib/overlook/csgo/demo/game_event_list_message_handler.rb +19 -0
- data/lib/overlook/csgo/demo/game_event_message_handler.rb +35 -0
- data/lib/overlook/csgo/demo/header.rb +45 -0
- data/lib/overlook/csgo/demo/key_reader.rb +18 -0
- data/lib/overlook/csgo/demo/packet.rb +16 -0
- data/lib/overlook/csgo/demo/packet_factory.rb +22 -0
- data/lib/overlook/csgo/demo/packet_handler.rb +50 -0
- data/lib/overlook/csgo/demo/parser.rb +66 -0
- data/lib/overlook/csgo/demo/user_message_message_handler.rb +43 -0
- data/lib/overlook/csgo/match_info_decoder.rb +92 -0
- data/lib/overlook/csgo/sharecode_decoder.rb +83 -0
- data/lib/overlook/version.rb +3 -0
- data/lib/proto/.gitkeep +0 -0
- data/lib/proto/compiled/cstrike15_gcmessages.pb.rb +1382 -0
- data/lib/proto/compiled/cstrike15_usermessages.pb.rb +702 -0
- data/lib/proto/compiled/descriptor.pb.rb +280 -0
- data/lib/proto/compiled/netmessages.pb.rb +687 -0
- data/lib/proto/compiled/steammessages.pb.rb +798 -0
- data/overlook-csgo.gemspec +36 -0
- metadata +209 -0
|
@@ -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,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,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
|