regulos 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|