csstats 1.1.0 → 1.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 +5 -5
- data/.rubocop.yml +33 -0
- data/.travis.yml +23 -7
- data/Gemfile +3 -7
- data/LICENSE +1 -1
- data/README.md +12 -9
- data/bin/console +1 -1
- data/csstats.gemspec +3 -4
- data/lib/csstats.rb +5 -3
- data/lib/csstats/client.rb +23 -0
- data/lib/csstats/columns.rb +28 -0
- data/lib/csstats/parser.rb +13 -4
- data/lib/csstats/parser/reader.rb +11 -0
- data/lib/csstats/parser/reader/file_reader.rb +45 -0
- data/lib/csstats/parser/{file_reader/handler.rb → reader/file_streamer.rb} +8 -8
- data/lib/csstats/parser/reader/player.rb +41 -0
- data/lib/csstats/parser/reader/players.rb +41 -0
- data/lib/csstats/{writer.rb → parser/writer.rb} +4 -2
- data/lib/csstats/{writer/file_writer/handler.rb → parser/writer/file_streamer.rb} +9 -10
- data/lib/csstats/parser/writer/player.rb +34 -0
- data/lib/csstats/parser/writer/players.rb +29 -0
- data/lib/csstats/player.rb +34 -0
- data/lib/csstats/players.rb +52 -0
- data/lib/csstats/version.rb +1 -1
- data/spec/csstats/client_spec.rb +13 -0
- data/spec/csstats/parser/reader/player_spec.rb +17 -0
- data/spec/csstats/parser/writer/players_spec.rb +30 -0
- data/spec/csstats/player_spec.rb +109 -0
- data/spec/csstats/players_spec.rb +30 -0
- data/spec/csstats_spec.rb +2 -2
- data/spec/spec_helper.rb +3 -7
- metadata +29 -21
- data/lib/csstats/handler.rb +0 -50
- data/lib/csstats/parser/file_reader.rb +0 -39
- data/lib/csstats/parser/player.rb +0 -82
- data/lib/csstats/parser/players.rb +0 -42
- data/lib/csstats/writer/player.rb +0 -39
- data/lib/csstats/writer/players.rb +0 -24
- data/spec/csstats/handler_spec.rb +0 -113
- data/spec/csstats/parser/player_spec.rb +0 -17
- data/spec/csstats/writer/players_spec.rb +0 -26
@@ -0,0 +1,41 @@
|
|
1
|
+
module CSstats
|
2
|
+
module Parser
|
3
|
+
module Reader
|
4
|
+
class Players
|
5
|
+
attr_reader :file_path, :max_players
|
6
|
+
|
7
|
+
def initialize(file_path, options = {})
|
8
|
+
@file_path = file_path
|
9
|
+
@max_players = options[:max_players] || 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse
|
13
|
+
players = []
|
14
|
+
|
15
|
+
file_reader.read do |streamer, index|
|
16
|
+
parse_player(streamer, index, players)
|
17
|
+
end
|
18
|
+
|
19
|
+
players
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def parse_player(streamer, index, players)
|
25
|
+
player_parser = CSstats::Parser::Reader::Player.new(streamer)
|
26
|
+
player = player_parser.parse
|
27
|
+
return unless player
|
28
|
+
|
29
|
+
player.rank = index + 1
|
30
|
+
players[index] = player
|
31
|
+
end
|
32
|
+
|
33
|
+
def file_reader
|
34
|
+
@file_reader ||= CSstats::Parser::Reader::FileReader.new(
|
35
|
+
file_path, limit: max_players
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,26 +1,25 @@
|
|
1
1
|
module CSstats
|
2
|
-
module
|
3
|
-
|
4
|
-
class
|
5
|
-
attr_reader :
|
2
|
+
module Parser
|
3
|
+
module Writer
|
4
|
+
class FileStreamer
|
5
|
+
attr_reader :stream
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@
|
7
|
+
def initialize(stream)
|
8
|
+
@stream = stream
|
9
9
|
end
|
10
10
|
|
11
11
|
def write_int_data(int)
|
12
12
|
data = [int].pack('V')
|
13
|
-
|
13
|
+
stream.write(data)
|
14
14
|
end
|
15
15
|
|
16
16
|
def write_short_data(int)
|
17
17
|
data = [int].pack('v')
|
18
|
-
|
18
|
+
stream.write(data)
|
19
19
|
end
|
20
20
|
|
21
21
|
def write_string_data(string)
|
22
|
-
|
23
|
-
handle.write(data)
|
22
|
+
stream.write(string)
|
24
23
|
end
|
25
24
|
end
|
26
25
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module CSstats
|
2
|
+
module Parser
|
3
|
+
module Writer
|
4
|
+
class Player
|
5
|
+
attr_reader :streamer, :player
|
6
|
+
|
7
|
+
def initialize(streamer, player)
|
8
|
+
@streamer = streamer
|
9
|
+
@player = player
|
10
|
+
end
|
11
|
+
|
12
|
+
def write
|
13
|
+
CSstats::COLUMNS.each do |column|
|
14
|
+
next streamer.write_int_data(0) if column.empty?
|
15
|
+
|
16
|
+
value = player.send(column[:name])
|
17
|
+
send("write_#{column[:type]}", value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def write_string(value)
|
24
|
+
streamer.write_short_data(value.length + 1)
|
25
|
+
streamer.write_string_data("#{value}\x00")
|
26
|
+
end
|
27
|
+
|
28
|
+
def write_integer(value)
|
29
|
+
streamer.write_int_data(value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module CSstats
|
2
|
+
module Parser
|
3
|
+
module Writer
|
4
|
+
class Players
|
5
|
+
attr_reader :file_path
|
6
|
+
|
7
|
+
def initialize(file_path)
|
8
|
+
@file_path = file_path
|
9
|
+
end
|
10
|
+
|
11
|
+
def write(players)
|
12
|
+
file = File.new(file_path, 'w')
|
13
|
+
|
14
|
+
streamer(file).write_short_data(11) # File version
|
15
|
+
|
16
|
+
players.each do |player|
|
17
|
+
CSstats::Parser::Writer::Player.new(streamer(file), player).write
|
18
|
+
end
|
19
|
+
|
20
|
+
file.close
|
21
|
+
end
|
22
|
+
|
23
|
+
def streamer(file)
|
24
|
+
CSstats::Parser::Writer::FileStreamer.new(file)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module CSstats
|
2
|
+
class Player
|
3
|
+
attr_accessor :rank
|
4
|
+
|
5
|
+
def initialize(attributes = {})
|
6
|
+
CSstats::COLUMN_KEYS.each do |column|
|
7
|
+
self.class.send(:attr_accessor, column)
|
8
|
+
end
|
9
|
+
|
10
|
+
attributes.each do |key, value|
|
11
|
+
instance_variable_set("@#{key}", value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def efficiency
|
16
|
+
kills_and_deaths = kills.to_f + deaths.to_f
|
17
|
+
return 0.0 if kills_and_deaths.zero?
|
18
|
+
|
19
|
+
(kills.to_f / kills_and_deaths * 100).round(2)
|
20
|
+
end
|
21
|
+
|
22
|
+
def accuracy
|
23
|
+
return 0.0 if shots.to_f.zero?
|
24
|
+
|
25
|
+
(hits.to_f / shots.to_f * 100).round(2)
|
26
|
+
end
|
27
|
+
|
28
|
+
def as_json
|
29
|
+
CSstats::COLUMN_KEYS.each_with_object({}) do |column, object|
|
30
|
+
object[column] = send(column)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module CSstats
|
2
|
+
class Players
|
3
|
+
attr_reader :client
|
4
|
+
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
def all
|
10
|
+
players
|
11
|
+
end
|
12
|
+
|
13
|
+
def find(id)
|
14
|
+
players[id - 1]
|
15
|
+
end
|
16
|
+
|
17
|
+
def find_by(attributes = {})
|
18
|
+
players.find do |player|
|
19
|
+
attributes.all? do |column, value|
|
20
|
+
player.send(column) == value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def where(attributes = {})
|
26
|
+
players.select do |player|
|
27
|
+
attributes.all? do |column, value|
|
28
|
+
player.send(column) == value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# def save
|
34
|
+
# CSstats::Parser.write_players(client, players)
|
35
|
+
# end
|
36
|
+
|
37
|
+
def method_missing(method_name, *args, &block)
|
38
|
+
return super unless respond_to?(method_name)
|
39
|
+
players.send(method_name, *args, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def respond_to_missing?(method_name, include_private = false)
|
43
|
+
players.respond_to?(method_name, include_private)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def players
|
49
|
+
@players ||= CSstats::Parser.get_players(client)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/csstats/version.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CSstats::Client do
|
4
|
+
let(:client) { CSstats::Client.new(path: csstats_file, max_players: 5) }
|
5
|
+
|
6
|
+
it 'has correct file path' do
|
7
|
+
expect(client.file_path).to eq(csstats_file)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'has correct max players value' do
|
11
|
+
expect(client.max_players).to eq(5)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CSstats::Parser::Reader::Player do
|
4
|
+
let(:file_stream) { File.new(csstats_file) }
|
5
|
+
let(:streamer) { CSstats::Parser::Reader::FileStreamer.new(file_stream) }
|
6
|
+
subject { described_class.new(streamer) }
|
7
|
+
let(:player_data) { subject.parse }
|
8
|
+
|
9
|
+
before do
|
10
|
+
# We need to read first bytes, because it's file version.
|
11
|
+
streamer.read_short_data
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'has correct head' do
|
15
|
+
expect(player_data.head).to eq(402)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CSstats::Parser::Writer::Players do
|
4
|
+
let(:old_players) { CSstats.new(path: csstats_file).players }
|
5
|
+
let(:file_path) { tmp_file('test.dat') }
|
6
|
+
let(:new_players) { CSstats.new(path: file_path).players }
|
7
|
+
|
8
|
+
it 'has correct players count in main file' do
|
9
|
+
expect(old_players.length).to eq(208)
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'new file' do
|
13
|
+
before do
|
14
|
+
described_class.new(file_path).write(old_players)
|
15
|
+
end
|
16
|
+
|
17
|
+
after do
|
18
|
+
FileUtils.rm(file_path)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'has correct players count' do
|
22
|
+
described_class.new(file_path).write(old_players)
|
23
|
+
expect(new_players.length).to eq(208)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'has same players data' do
|
27
|
+
expect(old_players.map(&:as_json)).to eq(new_players.map(&:as_json))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CSstats::Player do
|
4
|
+
let(:client) { CSstats::Client.new(path: csstats_file, max_players: 10) }
|
5
|
+
let(:players) { client.players }
|
6
|
+
|
7
|
+
it 'sets attributes from options' do
|
8
|
+
player = CSstats::Player.new(nick: 'Test')
|
9
|
+
expect(player.nick).to eq('Test')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'has ability to rewrite attribute' do
|
13
|
+
player = CSstats::Player.new
|
14
|
+
player.nick = 'Test'
|
15
|
+
expect(player.nick).to eq('Test')
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'data' do
|
19
|
+
let(:player) { players.find_by(nick: 'CHMARSON') }
|
20
|
+
|
21
|
+
it 'has correct nick' do
|
22
|
+
expect(player.nick).to eq 'CHMARSON'
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'has correct rank' do
|
26
|
+
expect(player.rank).to eq 2
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'has correct teamkill' do
|
30
|
+
expect(player.teamkill).to eq 7
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'has correct damage' do
|
34
|
+
expect(player.damage).to eq 101_543
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'has correct deaths' do
|
38
|
+
expect(player.deaths).to eq 511
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'has correct kills' do
|
42
|
+
expect(player.kills).to eq 759
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'has correct shots' do
|
46
|
+
expect(player.shots).to eq 9421
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'has correct hits' do
|
50
|
+
expect(player.hits).to eq 2885
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'has correct headshots' do
|
54
|
+
expect(player.headshots).to eq 225
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'has correct defusions' do
|
58
|
+
expect(player.defusions).to eq 15
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'has correct defused' do
|
62
|
+
expect(player.defused).to eq 9
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'has correct plants' do
|
66
|
+
expect(player.plants).to eq 33
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'has correct explosions' do
|
70
|
+
expect(player.explosions).to eq 7
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'has correct head' do
|
74
|
+
expect(player.head).to eq 275
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'has correct chest' do
|
78
|
+
expect(player.chest).to eq 407
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'has correct stomach' do
|
82
|
+
expect(player.stomach).to eq 376
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'has correct leftarm' do
|
86
|
+
expect(player.leftarm).to eq 1126
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'has correct rightarm' do
|
90
|
+
expect(player.rightarm).to eq 283
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'has correct leftleg' do
|
94
|
+
expect(player.leftleg).to eq 202
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'has correct rightleg' do
|
98
|
+
expect(player.rightleg).to eq 214
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'has correct accuracy' do
|
102
|
+
expect(player.accuracy).to eq 30.62
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'has correct efficiency' do
|
106
|
+
expect(player.efficiency).to eq 59.76
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CSstats::Players do
|
4
|
+
let(:client) { CSstats::Client.new(path: csstats_file, max_players: 30) }
|
5
|
+
let(:players) { client.players }
|
6
|
+
|
7
|
+
it 'has correct players count' do
|
8
|
+
expect(players.count).to eq(30)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns players with all' do
|
12
|
+
expect(players.all.length).to eq(30)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'has array of Player class' do
|
16
|
+
expect(players.first).to be_a(CSstats::Player)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'has correct player data' do
|
20
|
+
expect(players.find(2).nick).to eq('CHMARSON')
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'has correct count with where' do
|
24
|
+
expect(players.where(head: 102).count).to eq(2)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'has correct count with find_by' do
|
28
|
+
expect(players.find_by(head: 102).nick).to eq('LBM')
|
29
|
+
end
|
30
|
+
end
|