steamrb 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.rubocop.yml +4 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/README.md +116 -0
- data/Rakefile +21 -0
- data/bin/console +7 -0
- data/bin/setup +8 -0
- data/lib/ext/string.rb +42 -0
- data/lib/steam/byte_reader.rb +77 -0
- data/lib/steam/byte_writer.rb +96 -0
- data/lib/steam/client.rb +228 -0
- data/lib/steam/crypto.rb +113 -0
- data/lib/steam/emsg_util.rb +29 -0
- data/lib/steam/handler/auth.rb +78 -0
- data/lib/steam/handler/base.rb +79 -0
- data/lib/steam/handler/collection.rb +60 -0
- data/lib/steam/handler/game_coordinator.rb +80 -0
- data/lib/steam/handler/steam_apps.rb +35 -0
- data/lib/steam/handler/steam_user.rb +170 -0
- data/lib/steam/handler.rb +15 -0
- data/lib/steam/local_ip.rb +32 -0
- data/lib/steam/logger.rb +27 -0
- data/lib/steam/networking/connection.rb +178 -0
- data/lib/steam/networking/packet.rb +77 -0
- data/lib/steam/networking/packet_list.rb +27 -0
- data/lib/steam/networking.rb +14 -0
- data/lib/steam/plugins.rb +106 -0
- data/lib/steam/protocol/client_message.rb +13 -0
- data/lib/steam/protocol/gc_message.rb +9 -0
- data/lib/steam/protocol/gc_protobuf_message.rb +18 -0
- data/lib/steam/protocol/message.rb +99 -0
- data/lib/steam/protocol/protobuf_message.rb +54 -0
- data/lib/steam/protocol.rb +19 -0
- data/lib/steam/sentry_file.rb +42 -0
- data/lib/steam/server.rb +26 -0
- data/lib/steam/server_list.rb +34 -0
- data/lib/steam/version.rb +14 -0
- data/lib/steam.rb +46 -0
- data/steamrb.gemspec +37 -0
- metadata +213 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 99caa4cd2e21acd6cf5b3d53ce3079a6ca6a7a15
|
4
|
+
data.tar.gz: 3ccd1cc12a21b735f67d42d511b94a3c6a9079e2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2593c4948cd249f4464e3baabd18252ee7ba1b616cf81955cf5dddfa5303e63b65db3f3ff3ecc0ebb9c8472c50689a568aeb4fbba99cdf89e7b85aa2dd7753b8
|
7
|
+
data.tar.gz: 532adb1b7b9e99a5faff2e845d2a6f689b2684b0b88f181d69c77b65378dcac16e2b2704820309045b2a6a4b49e02f6cc9c0e48b1e5aceae41816e41bcac1a9d
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
# Steam
|
2
|
+
|
3
|
+
Ruby Client for Steam.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'steam'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install steam
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
To use you may subscribe a listener class to the Steam Client object in order
|
24
|
+
to react to events it fires.
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
require 'steam'
|
28
|
+
|
29
|
+
class MyClient < Steam::Client
|
30
|
+
def initialize(username, password)
|
31
|
+
super
|
32
|
+
@username = username
|
33
|
+
@password = password
|
34
|
+
@token = nil
|
35
|
+
|
36
|
+
file = SentryFile.new
|
37
|
+
@sha = file.read
|
38
|
+
end
|
39
|
+
|
40
|
+
def ready
|
41
|
+
steam_user.login(@username, @password, @token, @sha)
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_logon(msg)
|
45
|
+
case msg.body.eresult
|
46
|
+
when EResult::OK
|
47
|
+
# logged in
|
48
|
+
when EResult::ACCOUNT_LOGON_DENIED
|
49
|
+
puts 'Input token'
|
50
|
+
@token = STDIN.gets.chomp
|
51
|
+
restart
|
52
|
+
sleep 5
|
53
|
+
else
|
54
|
+
raise "Error: #{msg.body.eresult}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
bot = MyClient.new
|
60
|
+
bot.start
|
61
|
+
```
|
62
|
+
|
63
|
+
You may add game specific APIs by including them as a gem. CSGO plugin is provided by `fastpeek/steam-csgo`.
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
|
67
|
+
class MyClient < Steam::Client
|
68
|
+
def initialize(username, password)
|
69
|
+
super
|
70
|
+
@username = username
|
71
|
+
@password = password
|
72
|
+
@token = nil
|
73
|
+
|
74
|
+
file = SentryFile.new
|
75
|
+
@sha = file.read
|
76
|
+
end
|
77
|
+
|
78
|
+
def ready
|
79
|
+
steam_user.login(@username, @password, @token, @sha)
|
80
|
+
end
|
81
|
+
|
82
|
+
def csgo_match_info(match_info)
|
83
|
+
# do something with match_info
|
84
|
+
end
|
85
|
+
|
86
|
+
def csgo_ready
|
87
|
+
csgo.request_match_info(3_176_070_719_880_560_711,
|
88
|
+
3_176_076_024_165_171_409,
|
89
|
+
39_898)
|
90
|
+
end
|
91
|
+
|
92
|
+
def on_logon(msg)
|
93
|
+
case msg.body.eresult
|
94
|
+
when EResult::OK
|
95
|
+
csgo.start
|
96
|
+
when EResult::ACCOUNT_LOGON_DENIED
|
97
|
+
puts 'Input token'
|
98
|
+
@token = STDIN.gets.chomp
|
99
|
+
restart
|
100
|
+
else
|
101
|
+
raise "Error: #{msg.body.eresult}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
bot = MyClient.new
|
107
|
+
bot.plugin(:csgo)
|
108
|
+
bot.start
|
109
|
+
```
|
110
|
+
|
111
|
+
## License
|
112
|
+
|
113
|
+
MIT
|
114
|
+
|
115
|
+
## References
|
116
|
+
[SteamKit](https://github.com/SteamRE/SteamKit/)
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rubocop/rake_task'
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new('spec:units') do |task|
|
6
|
+
task.rspec_opts = '--tag ~integration'
|
7
|
+
end
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new('spec:integration') do |task|
|
10
|
+
task.rspec_opts = '--tag integration'
|
11
|
+
end
|
12
|
+
|
13
|
+
RuboCop::RakeTask.new do |task|
|
14
|
+
task.options = ['lib/']
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Run all specs and linter'
|
18
|
+
task 'spec:all' => ['spec:units', 'spec:integration'] do
|
19
|
+
end
|
20
|
+
|
21
|
+
task default: ['spec:units']
|
data/bin/console
ADDED
data/bin/setup
ADDED
data/lib/ext/string.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Core string extensions
|
4
|
+
class String
|
5
|
+
# Camelize a string
|
6
|
+
#
|
7
|
+
# @example Camelize a string
|
8
|
+
# "hello_world".camelize # => HelloWorld
|
9
|
+
def camelize
|
10
|
+
split('_').collect(&:capitalize).join
|
11
|
+
end
|
12
|
+
alias classify camelize
|
13
|
+
|
14
|
+
# Underscore a String. This method will also downcase the string
|
15
|
+
#
|
16
|
+
# @example Underscore a string
|
17
|
+
# "HelloWorld" # => "hello_world"
|
18
|
+
def underscore
|
19
|
+
gsub(/::/, '/')
|
20
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
21
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
22
|
+
.tr('-', '_')
|
23
|
+
.downcase
|
24
|
+
end
|
25
|
+
|
26
|
+
# Look up a constant via a string.
|
27
|
+
#
|
28
|
+
# @example Using constantize
|
29
|
+
# "Object".constantize # => Object
|
30
|
+
# rubocop:disable LineLength
|
31
|
+
def constantize
|
32
|
+
names = split('::')
|
33
|
+
names.shift if names.empty? || names.first.empty?
|
34
|
+
constant = Object
|
35
|
+
names.each do |name|
|
36
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
37
|
+
end
|
38
|
+
constant
|
39
|
+
end
|
40
|
+
|
41
|
+
alias snakecase underscore
|
42
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Steam
|
5
|
+
# Reads bytes from a given IO object.
|
6
|
+
class ByteReader
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
attr_reader :io
|
10
|
+
|
11
|
+
# Create a ByteReader object
|
12
|
+
#
|
13
|
+
# @param io [:read] an io object
|
14
|
+
def initialize(io)
|
15
|
+
@io = io
|
16
|
+
end
|
17
|
+
|
18
|
+
# Reads an unsigned 64 bit integer from the stream
|
19
|
+
#
|
20
|
+
# @return [Integer] The 64 bit integer
|
21
|
+
def read_int64
|
22
|
+
io.read(8).unpack('C*').each_with_index.reduce(0) do |sum, (byte, index)|
|
23
|
+
sum + byte * (256**index)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
alias int64 read_int64
|
27
|
+
|
28
|
+
# Reads an unsigned short from the stream
|
29
|
+
#
|
30
|
+
# @return [Integer] The short
|
31
|
+
def read_short
|
32
|
+
io.read(2).unpack('S*').first
|
33
|
+
end
|
34
|
+
alias short read_short
|
35
|
+
|
36
|
+
# Reads a string of a given length from the stream
|
37
|
+
#
|
38
|
+
# @return [String] The read string
|
39
|
+
def string(len)
|
40
|
+
io.read(len)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Reads a single bytes from the stream
|
44
|
+
#
|
45
|
+
# @return [Integer] The byte
|
46
|
+
def byte
|
47
|
+
io.read(1).ord
|
48
|
+
end
|
49
|
+
|
50
|
+
# Reads an unsigned 32 bit integer from the stream
|
51
|
+
#
|
52
|
+
# @return [Integer] The 32 bit integer
|
53
|
+
def unsigned_int32
|
54
|
+
io.read(4).unpack('<I*').first
|
55
|
+
end
|
56
|
+
|
57
|
+
# Reads an signed 32 bit integer from the stream
|
58
|
+
#
|
59
|
+
# @return [Integer] The 32 bit integer
|
60
|
+
def signed_int32
|
61
|
+
io.read(4).unpack('<i*').first
|
62
|
+
end
|
63
|
+
|
64
|
+
# Reads an signed 16 bit integer from the stream
|
65
|
+
#
|
66
|
+
# @return [Integer] The 16 bit integer
|
67
|
+
def signed_int16
|
68
|
+
io.read(2).unpack('<s*').first
|
69
|
+
end
|
70
|
+
|
71
|
+
def_delegator :@io, :read, :read
|
72
|
+
def_delegator :@io, :readbyte, :readbyte
|
73
|
+
def_delegator :@io, :eof?, :eof?
|
74
|
+
def_delegator :@io, :tell, :tell
|
75
|
+
def_delegator :@io, :lineno, :lineno
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Steam
|
5
|
+
# Writes bytes to a given io object
|
6
|
+
class ByteWriter
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
attr_reader :io
|
10
|
+
|
11
|
+
# Wrap an Int64 allowing the lo and hi
|
12
|
+
# 32 bits to be extracted
|
13
|
+
class Int64Type
|
14
|
+
# Create an Int64Type instance
|
15
|
+
#
|
16
|
+
# @param value [Integer] the 64 bit int
|
17
|
+
def initialize(value)
|
18
|
+
@value = value
|
19
|
+
end
|
20
|
+
|
21
|
+
# The low 32 bits
|
22
|
+
#
|
23
|
+
# @return [Integer]
|
24
|
+
def lo
|
25
|
+
@value & 0xFFFFFFFF
|
26
|
+
end
|
27
|
+
|
28
|
+
# The high 32 bits
|
29
|
+
#
|
30
|
+
# @return [Integer]
|
31
|
+
def hi
|
32
|
+
(@value >> 32) & 0xFFFFFFFF
|
33
|
+
end
|
34
|
+
|
35
|
+
# An array of the low and high bits
|
36
|
+
#
|
37
|
+
# @return [Integer]
|
38
|
+
def int32s
|
39
|
+
[lo, hi]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
@io = StringIO.new
|
45
|
+
@io.set_encoding('BINARY')
|
46
|
+
end
|
47
|
+
|
48
|
+
# Rewind the stream
|
49
|
+
def rewind
|
50
|
+
@io.rewind
|
51
|
+
end
|
52
|
+
|
53
|
+
# Writes an unsigned 32 bit integer from the stream
|
54
|
+
def write_unsigned_int32(value)
|
55
|
+
@io.write([value].pack('<I'))
|
56
|
+
end
|
57
|
+
|
58
|
+
# Writes an signed long from the stream
|
59
|
+
def write_signed_long(value)
|
60
|
+
@io.write([value].pack('<l_'))
|
61
|
+
end
|
62
|
+
|
63
|
+
# Writes a 32 bit intger to the stream
|
64
|
+
def write_int32(value)
|
65
|
+
@io.write([value].pack('<l'))
|
66
|
+
end
|
67
|
+
|
68
|
+
# Writes a 64 bit intger to the stream
|
69
|
+
def write_int64(value)
|
70
|
+
int64 = Int64Type.new(value)
|
71
|
+
|
72
|
+
int64.int32s.each do |int|
|
73
|
+
write_int32(int)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Writes a single byte to the stream
|
78
|
+
def write_byte(byte)
|
79
|
+
@io.write([byte].pack('C'))
|
80
|
+
end
|
81
|
+
|
82
|
+
# Writes the given bytes to the stream
|
83
|
+
def write(bytes)
|
84
|
+
@io.write(bytes)
|
85
|
+
end
|
86
|
+
alias write_string write
|
87
|
+
|
88
|
+
# The byte representation of this writer object
|
89
|
+
#
|
90
|
+
# @return [String] byte representation of this writer object
|
91
|
+
def string
|
92
|
+
@io.rewind
|
93
|
+
@io.string
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/steam/client.rb
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Steam
|
3
|
+
# Represents a Client on the Steam Network. The Client creates a connection
|
4
|
+
# to the Steam networks, and send and receives Message objects to that
|
5
|
+
# connection.
|
6
|
+
#
|
7
|
+
# The Client holds a collection of handlers that react to received packets,
|
8
|
+
# if they care about the packet data.
|
9
|
+
class Client
|
10
|
+
# The internal connection to Steam
|
11
|
+
attr_reader :connection
|
12
|
+
|
13
|
+
# The Handler for Steam user related events (changing name, login, etc)
|
14
|
+
attr_reader :steam_user
|
15
|
+
|
16
|
+
# The Handler for Steam app related events (play tokens, etc)
|
17
|
+
attr_reader :steam_apps
|
18
|
+
|
19
|
+
# The Handler for the game coordinator events (playing game, etc)
|
20
|
+
attr_reader :game_coordinator
|
21
|
+
|
22
|
+
# A list of tokens requires for the game coordinator to launch games
|
23
|
+
attr_reader :connect_tokens
|
24
|
+
|
25
|
+
# The SteamId for the current user, nil if not logged in
|
26
|
+
attr_reader :steam_id
|
27
|
+
|
28
|
+
# The session id for the current user, nilt if not logged in
|
29
|
+
attr_reader :session_id
|
30
|
+
|
31
|
+
# The plugin engine
|
32
|
+
attr_reader :plugins
|
33
|
+
|
34
|
+
def initialize(connection = nil)
|
35
|
+
connection ||= Networking::Connection.new(ServerList.new.to_a.sample)
|
36
|
+
@connect_tokens = []
|
37
|
+
@steam_user = nil
|
38
|
+
@steam_apps = nil
|
39
|
+
@game_coordinator = nil
|
40
|
+
@steam_id = default_steam_id
|
41
|
+
@connection = connection
|
42
|
+
@plugins = Plugins.new(self)
|
43
|
+
init_handlers
|
44
|
+
end
|
45
|
+
|
46
|
+
# Called by SteamUser handler when the logon message is to be handled.
|
47
|
+
# This must be implemented by the subclass
|
48
|
+
def on_logon(_msg)
|
49
|
+
raise NotImplementedError
|
50
|
+
end
|
51
|
+
|
52
|
+
# Called by SteamUser handler when the user is fully logged in. This must
|
53
|
+
# be implemented by the subclass.
|
54
|
+
def ready
|
55
|
+
raise NotImplementedError
|
56
|
+
end
|
57
|
+
|
58
|
+
# Registers a plugin with the Client via the plugin name
|
59
|
+
#
|
60
|
+
# @param plugin [Symbol,String] the plugin name
|
61
|
+
def plugin(plugin)
|
62
|
+
@plugins.plugin(plugin.to_sym)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Stop and start the Client
|
66
|
+
def restart
|
67
|
+
stop
|
68
|
+
start
|
69
|
+
end
|
70
|
+
|
71
|
+
# Disconnect the Client
|
72
|
+
def stop
|
73
|
+
steam_user.logoff if @steam_id && @session_id
|
74
|
+
connection.disconnect
|
75
|
+
@steam_id = default_steam_id
|
76
|
+
@session_id = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
# Create a new Connection object, and start listening and handling
|
80
|
+
# received packets.
|
81
|
+
#
|
82
|
+
# @return nil
|
83
|
+
def start
|
84
|
+
@plugins.load
|
85
|
+
|
86
|
+
connection.open
|
87
|
+
connection.each_packet do |packet|
|
88
|
+
handle_net_msg(packet)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Sends a Message to the underlying Steam connection. If we have an active
|
93
|
+
# session, the session id and steam id is automatically associated with
|
94
|
+
# the request
|
95
|
+
#
|
96
|
+
# @param msg [Protocol::Message] the Message to send to Steam
|
97
|
+
def send_msg(msg)
|
98
|
+
Steam.logger.debug("Sending #{msg.body.class.name} to Steam")
|
99
|
+
|
100
|
+
if msg.proto?
|
101
|
+
msg.session_id = session_id
|
102
|
+
msg.steam_id = steam_id.to_i
|
103
|
+
end
|
104
|
+
|
105
|
+
connection.send_msg(msg).nonzero?
|
106
|
+
end
|
107
|
+
|
108
|
+
# Creates a new session for the Client
|
109
|
+
#
|
110
|
+
# @param steam_id [String]
|
111
|
+
# @param session_id [String]
|
112
|
+
def create_session(steam_id, session_id)
|
113
|
+
@steam_id = CommunityId.new(steam_id)
|
114
|
+
@session_id = session_id
|
115
|
+
end
|
116
|
+
|
117
|
+
# Update the Client's internal connection token list
|
118
|
+
#
|
119
|
+
# @param tokens [Array<String>] the new list of tokens
|
120
|
+
# @param max [Integer] the amount of tokens to keep
|
121
|
+
def update_connect_tokens(tokens, max)
|
122
|
+
tokens.each { |t| @connect_tokens.insert(0, t) }
|
123
|
+
@connect_tokens = @connect_tokens.take(max)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Sets the Client's connection session key. Used for encrypting
|
127
|
+
# and decrypting packets
|
128
|
+
#
|
129
|
+
# @param key [String] the key
|
130
|
+
def session_key=(key)
|
131
|
+
connection.session_key = key
|
132
|
+
end
|
133
|
+
|
134
|
+
# Starts the background heartbeat thread.
|
135
|
+
#
|
136
|
+
# @param tick [Integer] the time to wait between
|
137
|
+
# heartbeat messages
|
138
|
+
def start_heartbeat(tick)
|
139
|
+
Thread.new do |t|
|
140
|
+
t.abort_on_exception = true
|
141
|
+
until connection.disconnected?
|
142
|
+
send_msg(heartbeat_message)
|
143
|
+
sleep(tick)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Passes each GC message to any plugins.
|
149
|
+
#
|
150
|
+
# @param msg [GcProtobufMessage] The message to handle
|
151
|
+
def gc_message(msg)
|
152
|
+
@plugins.loaded_plugins.each do |plugin|
|
153
|
+
send(plugin).gc_message(msg)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
# Instantiates the various handlers, and adds them to the handler
|
160
|
+
# collection
|
161
|
+
def init_handlers
|
162
|
+
@auth = Handler::Auth.new(self)
|
163
|
+
@steam_user = Handler::SteamUser.new(self)
|
164
|
+
@steam_apps = Handler::SteamApps.new(self)
|
165
|
+
@game_coordinator = Handler::GameCoordinator.new(self)
|
166
|
+
|
167
|
+
@handlers = Handler::Collection.new
|
168
|
+
@handlers.add(@auth, @steam_user, @steam_apps, @game_coordinator)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Handles an incoming packet, if the pack is a multi packet (ie: it contains
|
172
|
+
# more than one packet) each packet is extracted and passed back to this
|
173
|
+
# method.
|
174
|
+
#
|
175
|
+
# @param packet [Packet] the packet to handle
|
176
|
+
def handle_net_msg(packet)
|
177
|
+
return handle_multi(packet) if packet.proto? && packet.multi?
|
178
|
+
@handlers.handle(packet)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Handles a packet containing one or more other Packets. Sometimes
|
182
|
+
# the Packet data will be Gziped, this method handles that case as well.
|
183
|
+
#
|
184
|
+
# @note this method does not ensure the packet is a multi packet, the
|
185
|
+
# calling method should handle this
|
186
|
+
# @param packet [Networking::Packet] the packet to handle
|
187
|
+
def handle_multi(packet)
|
188
|
+
msg = packet.as_message(Steamclient::CMsgMulti.new)
|
189
|
+
|
190
|
+
body = StringIO.new(msg.body.message_body)
|
191
|
+
payload = if msg.body&.size_unzipped&.nonzero?
|
192
|
+
Zlib::GzipReader.new(body)
|
193
|
+
else
|
194
|
+
body
|
195
|
+
end
|
196
|
+
|
197
|
+
handle_packets_from_multi_payload(payload)
|
198
|
+
end
|
199
|
+
|
200
|
+
# Extracts one or more packet from a given payload IO object.
|
201
|
+
#
|
202
|
+
# @param payload [#read] The payload io object
|
203
|
+
def handle_packets_from_multi_payload(payload)
|
204
|
+
reader = ByteReader.new(payload)
|
205
|
+
|
206
|
+
until reader.eof?
|
207
|
+
packet_length = reader.unsigned_int32
|
208
|
+
handle_net_msg(Networking::Packet.new(reader.read(packet_length)))
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# The heartbeat message that we send once the user is connected
|
213
|
+
#
|
214
|
+
# @return [ProtobufMessage]
|
215
|
+
def heartbeat_message
|
216
|
+
ProtobufMessage.new(MsgHdrProtoBuf.new,
|
217
|
+
Steamclient::CMsgClientHeartBeat.new,
|
218
|
+
EMsg::CLIENT_HEART_BEAT)
|
219
|
+
end
|
220
|
+
|
221
|
+
# The default SteamID to be sent with each message before a user
|
222
|
+
# has been authed
|
223
|
+
# @return [Steam2Id]
|
224
|
+
def default_steam_id
|
225
|
+
Steam2Id.new(EUniverse::PUBLIC, 0, 0)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|