steamrb 0.1.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/.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
|