regulos 0.2.0 → 0.3.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.
- data/CHANGELOG.md +3 -0
- data/README.md +20 -5
- data/lib/regulos.rb +1 -0
- data/lib/regulos/combat_log/entity.rb +9 -0
- data/lib/regulos/combat_log/entity/base.rb +66 -0
- data/lib/regulos/combat_log/entity/npc.rb +8 -0
- data/lib/regulos/combat_log/entity/pet.rb +9 -0
- data/lib/regulos/combat_log/entity/player.rb +8 -0
- data/lib/regulos/combat_log/entity/unknown.rb +8 -0
- data/lib/regulos/combat_log/event/attack_hit.rb +1 -0
- data/lib/regulos/combat_log/event/attack_miss.rb +2 -0
- data/lib/regulos/combat_log/event/base.rb +71 -8
- data/lib/regulos/combat_log/event/begin_cast.rb +1 -0
- data/lib/regulos/combat_log/event/buff_fade.rb +1 -0
- data/lib/regulos/combat_log/event/buff_gain.rb +1 -0
- data/lib/regulos/combat_log/event/critical_heal.rb +2 -0
- data/lib/regulos/combat_log/event/critical_hit.rb +2 -0
- data/lib/regulos/combat_log/event/damage.rb +2 -1
- data/lib/regulos/combat_log/event/damage_over_time.rb +2 -0
- data/lib/regulos/combat_log/event/death.rb +1 -0
- data/lib/regulos/combat_log/event/debuff_fade.rb +1 -0
- data/lib/regulos/combat_log/event/debuff_gain.rb +2 -0
- data/lib/regulos/combat_log/event/dodge.rb +1 -0
- data/lib/regulos/combat_log/event/end_cast.rb +1 -0
- data/lib/regulos/combat_log/event/heal.rb +1 -0
- data/lib/regulos/combat_log/event/immune.rb +1 -0
- data/lib/regulos/combat_log/event/interrupt.rb +1 -0
- data/lib/regulos/combat_log/event/mana_gain.rb +1 -0
- data/lib/regulos/combat_log/event/miss.rb +9 -0
- data/lib/regulos/combat_log/event/parry.rb +2 -0
- data/lib/regulos/combat_log/event/resist.rb +3 -0
- data/lib/regulos/combat_log/event/unknown_death.rb +1 -0
- data/lib/regulos/combat_log/event_collection.rb +21 -24
- data/lib/regulos/combat_log/file.rb +76 -16
- data/lib/regulos/version.rb +1 -1
- metadata +10 -4
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -11,14 +11,29 @@
|
|
11
11
|
#### Usage
|
12
12
|
|
13
13
|
* require 'regulos'
|
14
|
-
* log = Regulos::CombatLog.
|
15
|
-
* log.
|
16
|
-
* events
|
17
|
-
*
|
18
|
-
|
14
|
+
* log = Regulos::CombatLog::File.new :path => "/path/to/combatlog.txt"
|
15
|
+
* log.read
|
16
|
+
* # Find all events that are heals that both target and originate from a player.
|
17
|
+
* log.select do |e|
|
18
|
+
* e.heal? \
|
19
|
+
* and \
|
20
|
+
* e.targets :player \
|
21
|
+
* and \
|
22
|
+
* e.origin_is :player
|
23
|
+
* end
|
24
|
+
|
25
|
+
#### Notes
|
26
|
+
The parser attempts to be as 'lazy' as possible. It does not read the entire file into memory, rather, it reads as needed.
|
19
27
|
|
20
28
|
#### Methods on Event
|
21
29
|
time, action_code, origin, target, origin_name, target_name, output, spell_id, spell_name, full_message
|
30
|
+
|
31
|
+
### TODO
|
32
|
+
- End of combat markers are not handled.
|
33
|
+
- Pet detection is surely broken.
|
34
|
+
- More convience methods for searching events, such as 'pvp?'
|
35
|
+
- Make file parsing smart in that events are grouped per end-of-combat marker
|
22
36
|
|
23
37
|
|
24
38
|
|
39
|
+
Author: James Cook
|
data/lib/regulos.rb
CHANGED
@@ -0,0 +1,66 @@
|
|
1
|
+
module Regulos
|
2
|
+
module CombatLog
|
3
|
+
module Entity
|
4
|
+
class Base
|
5
|
+
NIL_ENTITY = 'T=X#R=X#0'
|
6
|
+
attr_reader :guid, :name
|
7
|
+
def initialize options={}
|
8
|
+
@guid, @name = [ options[:guid], options[:name] ]
|
9
|
+
end
|
10
|
+
|
11
|
+
def inspect
|
12
|
+
"<#{self.class} '#{name}'>"
|
13
|
+
end
|
14
|
+
|
15
|
+
def is_a? thing
|
16
|
+
if thing.is_a?(Symbol)
|
17
|
+
thing.to_s.downcase == klass.downcase
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
other.respond_to?(:klass) && klass == other.klass && other.name == name
|
25
|
+
end
|
26
|
+
|
27
|
+
# Convience method to get the 'simple' class name
|
28
|
+
def klass
|
29
|
+
self.class.to_s.split("::").last
|
30
|
+
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
#T=N#R=O#9223372043800556155
|
34
|
+
#T=P#R=G#224054081475467319
|
35
|
+
def process hash
|
36
|
+
hash[:origin] = determine_entity(hash[:origin], hash[:origin_name])
|
37
|
+
hash[:target] = determine_entity(hash[:target], hash[:target_name])
|
38
|
+
hash[:pet_origin] = determine_entity(hash[:pet_origin], hash[:origin_name], true)
|
39
|
+
hash[:pet_target] = determine_entity(hash[:pet_target], hash[:target_name], true)
|
40
|
+
hash
|
41
|
+
end
|
42
|
+
|
43
|
+
def determine_entity flags_and_guid, name, pet=false
|
44
|
+
return nil if flags_and_guid == NIL_ENTITY || flags_and_guid.nil?
|
45
|
+
flags = flags_and_guid[0..8]
|
46
|
+
guid = flags_and_guid[9..-1]
|
47
|
+
|
48
|
+
entity_code = flags[2] #N,P,X
|
49
|
+
group_code = flags[6] #O,G,R # solo, group, & raid NYI
|
50
|
+
case entity_code
|
51
|
+
when 'N' then
|
52
|
+
klass = Npc
|
53
|
+
when 'P' then
|
54
|
+
klass = pet ? Pet : Player
|
55
|
+
when 'X' then
|
56
|
+
klass = Unknown
|
57
|
+
else
|
58
|
+
klass = Unknown
|
59
|
+
end
|
60
|
+
klass.new(:guid => guid, :name => name)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -39,6 +39,35 @@ module Regulos
|
|
39
39
|
"<#{self.class} id=#{id}>"
|
40
40
|
end
|
41
41
|
|
42
|
+
def to_s
|
43
|
+
respond_to?(:full_message) ? full_message : inspect
|
44
|
+
end
|
45
|
+
|
46
|
+
def is_a? thing
|
47
|
+
if thing.is_a?(Symbol)
|
48
|
+
thing.to_s.downcase == name.downcase
|
49
|
+
else
|
50
|
+
super
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def is_not? thing
|
55
|
+
!is_a?(thing)
|
56
|
+
end
|
57
|
+
|
58
|
+
def targets thing
|
59
|
+
target && target.is_a?(thing)
|
60
|
+
end
|
61
|
+
|
62
|
+
def originates thing
|
63
|
+
origin && origin.is_a?(thing)
|
64
|
+
end
|
65
|
+
alias_method :origin_is, :originates
|
66
|
+
|
67
|
+
def is_event?
|
68
|
+
true
|
69
|
+
end
|
70
|
+
|
42
71
|
def id
|
43
72
|
@id ||= Digest::MD5.hexdigest( [time, full_message].join ) if time && full_message
|
44
73
|
end
|
@@ -46,6 +75,11 @@ module Regulos
|
|
46
75
|
def ==(other)
|
47
76
|
other.id == id
|
48
77
|
end
|
78
|
+
alias :eql? :==
|
79
|
+
|
80
|
+
def <=> other
|
81
|
+
time.to_s <=> other.time.to_s
|
82
|
+
end
|
49
83
|
|
50
84
|
def name
|
51
85
|
self.class.to_s.split("::").last
|
@@ -55,22 +89,45 @@ module Regulos
|
|
55
89
|
CODES.invert[ action_code ]
|
56
90
|
end
|
57
91
|
|
58
|
-
def
|
59
|
-
|
92
|
+
def target_is_player?
|
93
|
+
target && target.klass == "Player"
|
94
|
+
end
|
95
|
+
|
96
|
+
def target_is_npc?
|
97
|
+
target && target.klass == "Npc"
|
60
98
|
end
|
61
99
|
|
62
|
-
def
|
63
|
-
|
100
|
+
def origin_is_npc?
|
101
|
+
origin && origin.klass == "Npc"
|
64
102
|
end
|
65
103
|
|
66
|
-
def
|
67
|
-
|
104
|
+
def origin_is_player?
|
105
|
+
origin && origin.klass == "Player"
|
68
106
|
end
|
69
107
|
|
70
|
-
def
|
71
|
-
|
108
|
+
def origin_is_pet?
|
109
|
+
origin && origin.klass == "Pet"
|
72
110
|
end
|
73
111
|
|
112
|
+
def target_is_pet?
|
113
|
+
target && target.klass == "Pet"
|
114
|
+
end
|
115
|
+
|
116
|
+
def miss?; false; end
|
117
|
+
def parry?; false; end
|
118
|
+
def dodge?; false; end
|
119
|
+
def resist?; false; end
|
120
|
+
def immune?; false; end
|
121
|
+
def attack?; false; end
|
122
|
+
def spell?; false; end
|
123
|
+
def heal?; false; end
|
124
|
+
def overheal?; false; end
|
125
|
+
def buff?; false; end
|
126
|
+
def debuff?; false; end
|
127
|
+
def critical?; false; end
|
128
|
+
def death?; false; end
|
129
|
+
def damage_over_time?; false; end
|
130
|
+
|
74
131
|
def process attributes
|
75
132
|
attributes.each_pair{|k,v| self.class.send(:attr_reader, k); instance_variable_set("@#{k}", v) }
|
76
133
|
end
|
@@ -80,6 +137,7 @@ module Regulos
|
|
80
137
|
class << self
|
81
138
|
def which(row)
|
82
139
|
require "strscan"
|
140
|
+
return row if row.class.to_s =~ /Event\Z/i
|
83
141
|
s = StringScanner.new row
|
84
142
|
#03:53:35: ( 15 , T=N#R=O#9223372038886130583 , T=N#R=O#9223372043800556153 , T=X#R=X#224054081475471412 , T=X#R=X#0 , Lesser Earth Elemental , Eternal Servant , 0 , 75533189 , Thud ) Eternal Servant dodges Lesser Earth Elemental's Thud.
|
85
143
|
entity_regex = /\s[A-Z]=[A-Z]#[A-Z]=[A-Z]#\d+?\s,/
|
@@ -97,9 +155,14 @@ module Regulos
|
|
97
155
|
e[:spell_name] = s.scan /\s[A-Za-z\-\s]+?\s\)/ # Spell name
|
98
156
|
e[:full_message] = s.scan /\s(.*)+\Z/ # Full log message
|
99
157
|
e = sanitize(e)
|
158
|
+
e = capture_entities(e)
|
100
159
|
determine_event_type(e).new e
|
101
160
|
end
|
102
161
|
|
162
|
+
def capture_entities(hash)
|
163
|
+
CombatLog::Entity::Base.process(hash)
|
164
|
+
end
|
165
|
+
|
103
166
|
def sanitize hash
|
104
167
|
hash.each_pair do |k,v|
|
105
168
|
v.to_s.strip! # Remove leading space
|
@@ -1,44 +1,41 @@
|
|
1
|
-
require "
|
1
|
+
require "set"
|
2
|
+
|
2
3
|
module Regulos
|
3
4
|
module CombatLog
|
4
|
-
class EventCollection <
|
5
|
-
|
6
|
-
|
7
|
-
attr_reader :events
|
5
|
+
class EventCollection < SortedSet
|
6
|
+
alias_method :all, :select
|
8
7
|
|
9
8
|
def initialize(events=[])
|
10
9
|
@events = events
|
11
|
-
super(
|
10
|
+
super(events)
|
12
11
|
end
|
13
12
|
|
14
|
-
def
|
15
|
-
|
13
|
+
def events
|
14
|
+
self
|
16
15
|
end
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
raise FilterException, "You can't specify both 'only' and 'exclude'."
|
17
|
+
def [](index)
|
18
|
+
if index && index.is_a?(Range)
|
19
|
+
return EventCollection.new( to_a[index.begin, index.end] )
|
20
|
+
else
|
21
|
+
return to_a[index]
|
24
22
|
end
|
23
|
+
end
|
25
24
|
|
26
|
-
|
27
|
-
|
25
|
+
def select &block
|
26
|
+
EventCollection.new( to_a.select(&block) )
|
28
27
|
end
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
filter_events(:only, options[:event]) if options[:event]
|
29
|
+
def inspect
|
30
|
+
"<#{self.class} size=#{size}"
|
33
31
|
end
|
34
32
|
|
35
|
-
def
|
36
|
-
|
33
|
+
def first
|
34
|
+
to_a.first
|
37
35
|
end
|
38
36
|
|
39
|
-
def
|
40
|
-
|
41
|
-
EventCollection.new send(mode){|e| keys.include?(e.name) }
|
37
|
+
def last
|
38
|
+
to_a.last
|
42
39
|
end
|
43
40
|
end
|
44
41
|
end
|
@@ -1,33 +1,93 @@
|
|
1
|
+
# ############################ #
|
2
|
+
# Regulos - Combat log parser #
|
3
|
+
# ############################ #
|
4
|
+
#
|
5
|
+
# f = Regulos::CombatLog::File.new :path => "/Users/jamecook/Downloads/CombatLog.txt"
|
6
|
+
# f.read # Read all events, not required
|
7
|
+
# f.first
|
8
|
+
# f.first{|event| event.is_a?(:heal) }
|
9
|
+
# See Event::Base for the various methods that can be used for filtering.
|
10
|
+
#
|
11
|
+
|
1
12
|
module Regulos
|
2
13
|
module CombatLog
|
3
14
|
class FileNotFoundError < RuntimeError; end
|
4
|
-
# Helper for .new
|
5
|
-
def CombatLog.parse(path)
|
6
|
-
log = File.new( :path => path )
|
7
|
-
log.parse
|
8
|
-
log
|
9
|
-
end
|
10
15
|
|
11
|
-
class File
|
16
|
+
class File < ::File
|
12
17
|
|
13
|
-
attr_reader :path, :events
|
18
|
+
attr_reader :path, :events, :debug
|
14
19
|
def initialize(options={})
|
15
|
-
@path
|
20
|
+
@path = File.exist?( options[:path] ) ? options[:path] : raise(FileNotFoundError, "The specified combat log does not exist.")
|
16
21
|
@events = EventCollection.new []
|
22
|
+
super(@path)
|
17
23
|
end
|
18
24
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
25
|
+
def inspect
|
26
|
+
"<Regulos::CombatLog::File path=#{@path}>"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Pass the block onto the collection set for searching
|
30
|
+
# e.g. select{|event| event.is_a?(:heal) }
|
31
|
+
def each &block
|
32
|
+
if eof?
|
33
|
+
@events.instance_eval do
|
34
|
+
each &block
|
35
|
+
end
|
36
|
+
else
|
37
|
+
trap("INT"){ rewind; break } # if you SIGINT mid search, future searches will be goofed until eof is
|
38
|
+
# reached due to the lazy nature of this block.
|
39
|
+
each_line do |e|
|
40
|
+
parsed = Event::Base.which(e)
|
41
|
+
@events << parsed
|
42
|
+
STDERR.write("\r-> Reading (#{pos}) '#{parsed.name}' ") if debug?
|
43
|
+
yield parsed
|
44
|
+
end
|
45
|
+
end
|
22
46
|
end
|
23
47
|
|
24
|
-
def
|
25
|
-
|
48
|
+
def select &block
|
49
|
+
EventCollection.new super
|
26
50
|
end
|
27
51
|
|
28
|
-
def
|
29
|
-
|
52
|
+
def size
|
53
|
+
@size || read.size
|
54
|
+
end
|
55
|
+
|
56
|
+
# Resets the event collection and re-parse the log.
|
57
|
+
def reload
|
58
|
+
@events.clear
|
59
|
+
read
|
60
|
+
end
|
61
|
+
|
62
|
+
def read
|
63
|
+
readlines.map{|line| @events << Event::Base.which(line) }
|
64
|
+
if eof?
|
65
|
+
@size = @events.size
|
66
|
+
end
|
67
|
+
@events
|
68
|
+
end
|
69
|
+
|
70
|
+
def first &block
|
71
|
+
if block_given?
|
72
|
+
detect &block
|
73
|
+
else
|
74
|
+
@events.first || detect{|e| e.is_event? }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Reverse the set and pluck the first match. This requires the entire file to be parsed (yikes).
|
79
|
+
def last &block
|
80
|
+
read unless eof?
|
81
|
+
if block_given?
|
82
|
+
EventCollection.new(@events.to_a.reverse).detect &block
|
83
|
+
else
|
84
|
+
@events[-1]
|
85
|
+
end
|
30
86
|
end
|
87
|
+
|
88
|
+
# Enable debugging, which currently prints parsing information during a search.
|
89
|
+
def debug!; @debug=true; end
|
90
|
+
def debug?; @debug; end
|
31
91
|
end
|
32
92
|
end
|
33
93
|
end
|
data/lib/regulos/version.rb
CHANGED
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 3
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.3.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- James Cook
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-
|
17
|
+
date: 2011-03-08 00:00:00 -06:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -40,6 +40,12 @@ extensions: []
|
|
40
40
|
extra_rdoc_files: []
|
41
41
|
|
42
42
|
files:
|
43
|
+
- lib/regulos/combat_log/entity/base.rb
|
44
|
+
- lib/regulos/combat_log/entity/npc.rb
|
45
|
+
- lib/regulos/combat_log/entity/pet.rb
|
46
|
+
- lib/regulos/combat_log/entity/player.rb
|
47
|
+
- lib/regulos/combat_log/entity/unknown.rb
|
48
|
+
- lib/regulos/combat_log/entity.rb
|
43
49
|
- lib/regulos/combat_log/event/attack_hit.rb
|
44
50
|
- lib/regulos/combat_log/event/attack_miss.rb
|
45
51
|
- lib/regulos/combat_log/event/base.rb
|
@@ -73,7 +79,7 @@ files:
|
|
73
79
|
- README.md
|
74
80
|
- CHANGELOG.md
|
75
81
|
has_rdoc: true
|
76
|
-
homepage: http://github.com/
|
82
|
+
homepage: http://github.com/jamescook/regulos
|
77
83
|
licenses: []
|
78
84
|
|
79
85
|
post_install_message:
|