csstats 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|