tf2_line_parser 0.6.5 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f18e216a4247d37720754f3a66281d93c88af477875335fd9a3d3a79228f2a8b
4
- data.tar.gz: f3ce9a46e96f0b37820c02e5b876f98bf8232fff6e42733ebd79727a4fd9e46a
3
+ metadata.gz: c9ae1ffbaaa2ebff7e8cf30040ef166b57c12285cb2870f397f968c8daf9a883
4
+ data.tar.gz: 9245015c50d5de623077004a90cd79c45c406094a082220ac1282ecee1fc0ca6
5
5
  SHA512:
6
- metadata.gz: d9daf5c5f614d817d09d8de35539f5d7acbc61bbb7b47863e4a12551cdae2a00ba521ede013cfa485eed863520a157007496a34173dfdb46b388dcdb9dd7934e
7
- data.tar.gz: 985510a7d6108b80db1ae408e32dfa6e0d5da41293b4f50d74abae403a79f3ad4774b750693d85438e86969f48f2d1373fd2dbcc79a533e33e5784de777a9030
6
+ metadata.gz: 8eb08df2bf3d064ed0bb24cc63ab5e0f78f12b6d59ed019f6f3fabb698e562a906d799d64f643462781a33122ebfab2971686beb2e4bf08a30b6fe59fa8305eb
7
+ data.tar.gz: c359b30e53b652a2a889692199e6adb44821c4e0368d8793ce6773dffaca13e9aee58026f7e1d1bc1e72675fd9bd47d79f6a4a1ae185f60660ebcdde6551ada3
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tf2_line_parser (0.6.2)
4
+ tf2_line_parser (0.7.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class CarryObject < ObjectEvent
6
+ def self.keyword
7
+ 'player_carryobject'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -14,7 +14,7 @@ module TF2LineParser
14
14
  end
15
15
 
16
16
  def self.regex
17
- @regex ||= /#{regex_time} #{regex_player} disconnected \(reason #{regex_message}\)/.freeze
17
+ @regex ||= /#{regex_time} #{regex_player} disconnected \(reason "(?'message'.*?)(?:"\)\s*$|$)/.freeze
18
18
  end
19
19
  end
20
20
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class DropObject < ObjectEvent
6
+ def self.keyword
7
+ 'player_dropobject'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -48,7 +48,7 @@ module TF2LineParser
48
48
  @types ||= [ShotFired, ShotHit, PositionReport, HeadshotDamage, Airshot, Damage, AirshotHeal, Heal, PickupItem, Assist, Kill, CaptureBlock, PointCapture, ChargeDeployed,
49
49
  MedicDeath, MedicDeathEx, EmptyUber, ChargeReady, ChargeEnded, FirstHealAfterSpawn, LostUberAdvantage, BuiltObject, PlayerExtinguished, JoinedTeam, EnteredGame, RoleChange, Spawn, Suicide, KilledObject, Say, TeamSay, Domination, Revenge, RoundWin, CurrentScore,
50
50
  RoundLength, RoundStart, RoundSetupBegin, RoundSetupEnd, MiniRoundStart, MiniRoundSelected, MiniRoundLength, MiniRoundWin, WorldIntermissionWinLimit, IntermissionWinLimit, Connect, Disconnect, RconCommand, ConsoleSay, MatchEnd, FinalScore,
51
- RoundStalemate, Unknown].freeze
51
+ RoundStalemate, ServerCvar, CarryObject, DropObject, ObjectDetonated, SteamUseridValidated, LogFileStarted, LogFileClosed, LoadingMap, MapStarted, MetaData, RoundOvertime, GamePaused, GameUnpaused, PauseLength, MatchPause, MatchUnpause, NameChange, Unknown].freeze
52
52
  end
53
53
 
54
54
  def self.regex_results(matched_line)
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class GamePaused < RoundEventWithoutVariables
6
+ def self.round_type
7
+ @round_type ||= 'Game_Paused'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class GameUnpaused < RoundEventWithoutVariables
6
+ def self.round_type
7
+ @round_type ||= 'Game_Unpaused'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -4,7 +4,7 @@ module TF2LineParser
4
4
  attr_reader :time, :team, :reason
5
5
 
6
6
  def self.regex
7
- @regex ||= /#{regex_time} Team "(?'team'Red|Blue|RED|BLUE)" triggered "Intermission_Win_Limit" due to (?'reason'.*)/.freeze
7
+ @regex ||= /#{regex_time} Team "(?'team'Red|Blue|RED|BLUE)" triggered "Intermission_Win_Limit"(?: due to (?'reason'.*))?/.freeze
8
8
  end
9
9
 
10
10
  def self.attributes
@@ -1,14 +1,14 @@
1
1
  module TF2LineParser
2
2
  module Events
3
3
  class KilledObject < Event
4
- attr_reader :time, :player, :object, :weapon, :objectowner
4
+ attr_reader :time, :player, :object, :weapon, :objectowner, :assist
5
5
 
6
6
  def self.regex
7
7
  @regex ||= /#{regex_time} #{regex_player} triggered "killedobject" #{regex_object_info}#{regex_attacker_position}?/.freeze
8
8
  end
9
9
 
10
10
  def self.regex_object_info
11
- @regex_object_info ||= '\(object "(?\'object\'[^"]+)"\) \(weapon "(?\'weapon\'[^"]+)"\) \(objectowner "(?\'objectowner_section\'.*?)"\)'
11
+ @regex_object_info ||= '\(object "(?\'object\'[^"]+)"\)(?: \(weapon "(?\'weapon\'[^"]+)"\))? \(objectowner "(?\'objectowner_section\'.*?)"\)(?: \(assist "(?\'assist\'\d+)"\))?'
12
12
  end
13
13
 
14
14
  def self.regex_attacker_position
@@ -16,7 +16,7 @@ module TF2LineParser
16
16
  end
17
17
 
18
18
  def self.attributes
19
- @attributes ||= %i[time player_section object weapon objectowner_section]
19
+ @attributes ||= %i[time player_section object weapon objectowner_section assist]
20
20
  end
21
21
 
22
22
  def self.regex_results(matched_line)
@@ -42,12 +42,13 @@ module TF2LineParser
42
42
  out
43
43
  end
44
44
 
45
- def initialize(time, player_name, player_uid, player_steam_id, player_team, object, weapon, objectowner_name, objectowner_uid, objectowner_steam_id, objectowner_team)
45
+ def initialize(time, player_name, player_uid, player_steam_id, player_team, object, weapon, objectowner_name, objectowner_uid, objectowner_steam_id, objectowner_team, assist = nil)
46
46
  @time = parse_time(time)
47
47
  @player = Player.new(player_name, player_uid, player_steam_id, player_team)
48
48
  @object = object
49
49
  @weapon = weapon
50
50
  @objectowner = Player.new(objectowner_name, objectowner_uid, objectowner_steam_id, objectowner_team)
51
+ @assist = assist == '1'
51
52
  end
52
53
  end
53
54
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class LoadingMap < Event
6
+ attr_reader :time, :map
7
+
8
+ def self.regex
9
+ @regex ||= /#{regex_time} Loading map "(?'map'[^"]*)"/.freeze
10
+ end
11
+
12
+ def self.attributes
13
+ @attributes ||= %i[time map]
14
+ end
15
+
16
+ def initialize(time, map)
17
+ @time = parse_time(time)
18
+ @map = map
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class LogFileClosed < Event
6
+ def self.regex
7
+ @regex ||= /#{regex_time} Log file closed\.?\s*$/.freeze
8
+ end
9
+
10
+ def self.attributes
11
+ @attributes ||= [:time]
12
+ end
13
+
14
+ def initialize(time)
15
+ @time = parse_time(time)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class LogFileStarted < Event
6
+ attr_reader :time, :file, :game, :version
7
+
8
+ def self.regex
9
+ @regex ||= /#{regex_time} Log file started \(file "(?'file'[^"]*)"\) \(game "(?'game'[^"]*)"\) \(version "(?'version'[^"]*)"\)/.freeze
10
+ end
11
+
12
+ def self.attributes
13
+ @attributes ||= %i[time file game version]
14
+ end
15
+
16
+ def initialize(time, file, game, version)
17
+ @time = parse_time(time)
18
+ @file = file
19
+ @game = game
20
+ @version = version
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class MapStarted < Event
6
+ attr_reader :time, :map, :crc
7
+
8
+ def self.regex
9
+ @regex ||= /#{regex_time} Started map "(?'map'[^"]*)" \(CRC "(?'crc'[^"]*)"\)/.freeze
10
+ end
11
+
12
+ def self.attributes
13
+ @attributes ||= %i[time map crc]
14
+ end
15
+
16
+ def initialize(time, map, crc)
17
+ @time = parse_time(time)
18
+ @map = map
19
+ @crc = crc
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class MatchPause < Event
6
+ attr_reader :time, :player
7
+
8
+ def self.regex
9
+ @regex ||= /#{regex_time} #{regex_player} triggered "matchpause"/.freeze
10
+ end
11
+
12
+ def self.attributes
13
+ @attributes ||= %i[time player_section]
14
+ end
15
+
16
+ def initialize(time, player_name, player_uid, player_steam_id, player_team)
17
+ @time = parse_time(time)
18
+ @player = Player.new(player_name, player_uid, player_steam_id, player_team)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class MatchUnpause < Event
6
+ attr_reader :time, :player
7
+
8
+ def self.regex
9
+ @regex ||= /#{regex_time} #{regex_player} triggered "matchunpause"/.freeze
10
+ end
11
+
12
+ def self.attributes
13
+ @attributes ||= %i[time player_section]
14
+ end
15
+
16
+ def initialize(time, player_name, player_uid, player_steam_id, player_team)
17
+ @time = parse_time(time)
18
+ @player = Player.new(player_name, player_uid, player_steam_id, player_team)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class MetaData < Event
6
+ attr_reader :time, :key, :value
7
+
8
+ def self.regex
9
+ @regex ||= /#{regex_time} World triggered "meta_data" \((?'key'\w+) "(?'value'.*)"\)/.freeze
10
+ end
11
+
12
+ def self.attributes
13
+ @attributes ||= %i[time key value]
14
+ end
15
+
16
+ def initialize(time, key, value)
17
+ @time = parse_time(time)
18
+ @key = key
19
+ @value = value
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class NameChange < Event
6
+ attr_reader :time, :player, :new_name
7
+
8
+ # The old name must end in a <uid><steamid><team> tag, so a name that merely
9
+ # contains ' changed name to ' can't make another line look like a name change.
10
+ def self.regex
11
+ @regex ||= /#{regex_time} "(?<player_section>.*?<\d+><[^<>"]*><[^<>"]*>)" changed name to "(?'new_name'.*)"/.freeze
12
+ end
13
+
14
+ def self.attributes
15
+ @attributes ||= %i[time player_section new_name]
16
+ end
17
+
18
+ def initialize(time, player_name, player_uid, player_steam_id, player_team, new_name)
19
+ @time = parse_time(time)
20
+ @player = Player.new(player_name, player_uid, player_steam_id, player_team)
21
+ @new_name = new_name
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class ObjectDetonated < ObjectEvent
6
+ def self.keyword
7
+ 'object_detonated'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class ObjectEvent < Event
6
+ attr_reader :time, :player, :object, :position
7
+
8
+ def self.regex
9
+ @regex ||= /#{regex_time} #{regex_player} triggered "#{keyword}" \(object "(?'object'[^"]+)"\)(?: \(position "(?'position'[^"]+)"\))?/.freeze
10
+ end
11
+
12
+ def self.attributes
13
+ @attributes ||= %i[time player_section object position]
14
+ end
15
+
16
+ def initialize(time, player_name, player_uid, player_steam_id, player_team, object, position)
17
+ @time = parse_time(time)
18
+ @player = Player.new(player_name, player_uid, player_steam_id, player_team)
19
+ @object = object
20
+ @position = position
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class PauseLength < RoundEventWithVariables
6
+ def self.round_type
7
+ @round_type ||= 'Pause_Length'
8
+ end
9
+
10
+ def self.round_variable_regex
11
+ @round_variable_regex ||= /\(seconds "(?'length'.*)"\)/.freeze
12
+ end
13
+
14
+ def self.round_variable
15
+ @round_variable ||= :length
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class RoundOvertime < RoundEventWithoutVariables
6
+ def self.round_type
7
+ @round_type ||= 'Round_Overtime'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class ServerCvar < Event
6
+ attr_reader :time, :name, :value
7
+
8
+ def self.regex
9
+ @regex ||= /#{regex_time} server_cvar: "(?'name'[^"]*)" "(?'value'.*)"/.freeze
10
+ end
11
+
12
+ def self.attributes
13
+ @attributes ||= %i[time name value]
14
+ end
15
+
16
+ def initialize(time, name, value)
17
+ @time = parse_time(time)
18
+ @name = name
19
+ @value = value
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TF2LineParser
4
+ module Events
5
+ class SteamUseridValidated < Event
6
+ attr_reader :time, :player
7
+
8
+ def self.regex
9
+ @regex ||= /#{regex_time} #{regex_player} STEAM USERID validated\s*$/.freeze
10
+ end
11
+
12
+ def self.attributes
13
+ @attributes ||= %i[time player_section]
14
+ end
15
+
16
+ def initialize(time, player_name, player_uid, player_steam_id, player_team)
17
+ @time = parse_time(time)
18
+ @player = Player.new(player_name, player_uid, player_steam_id, player_team)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -28,6 +28,9 @@ module TF2LineParser
28
28
  'lost_uber_advantage' => [Events::LostUberAdvantage],
29
29
  'builtobject' => [Events::BuiltObject],
30
30
  'player_builtobject' => [Events::BuiltObject],
31
+ 'player_carryobject' => [Events::CarryObject],
32
+ 'player_dropobject' => [Events::DropObject],
33
+ 'object_detonated' => [Events::ObjectDetonated],
31
34
  'player_extinguished' => [Events::PlayerExtinguished],
32
35
  'joined team' => [Events::JoinedTeam],
33
36
  'entered the game' => [Events::EnteredGame],
@@ -46,6 +49,13 @@ module TF2LineParser
46
49
  'Mini_Round_Length' => [Events::MiniRoundLength],
47
50
  'Mini_Round_Win' => [Events::MiniRoundWin],
48
51
  'Intermission_Win_Limit' => [Events::WorldIntermissionWinLimit, Events::IntermissionWinLimit],
52
+ 'meta_data' => [Events::MetaData],
53
+ 'Round_Overtime' => [Events::RoundOvertime],
54
+ 'Game_Paused' => [Events::GamePaused],
55
+ 'Game_Unpaused' => [Events::GameUnpaused],
56
+ 'Pause_Length' => [Events::PauseLength],
57
+ 'matchpause' => [Events::MatchPause],
58
+ 'matchunpause' => [Events::MatchUnpause],
49
59
  'Round_Stalemate' => [Events::RoundStalemate],
50
60
  'Game_Over' => [Events::MatchEnd],
51
61
  'connected, address' => [Events::Connect],
@@ -62,7 +72,14 @@ module TF2LineParser
62
72
  Events::JoinedTeam, Events::EnteredGame, Events::RoleChange,
63
73
  Events::Spawn, Events::Suicide, Events::Connect, Events::Disconnect,
64
74
  Events::CurrentScore, Events::FinalScore, Events::RconCommand,
65
- Events::ConsoleSay, Events::Unknown
75
+ Events::ConsoleSay, Events::ServerCvar,
76
+ Events::SteamUseridValidated,
77
+ Events::LogFileStarted,
78
+ Events::LogFileClosed,
79
+ Events::LoadingMap,
80
+ Events::MapStarted,
81
+ Events::NameChange,
82
+ Events::Unknown
66
83
  ].freeze
67
84
 
68
85
  CHAT_REGEX = /#{Events::Event.regex_time} #{Events::Event.regex_chat_player} (?<chat_type>say|say_team) "/
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TF2LineParser
4
- VERSION = '0.6.5'
4
+ VERSION = '0.7.0'
5
5
  end
@@ -14,6 +14,7 @@ require 'tf2_line_parser/events/score'
14
14
  require 'tf2_line_parser/events/role_change'
15
15
  require 'tf2_line_parser/events/damage'
16
16
  require 'tf2_line_parser/events/heal'
17
+ require 'tf2_line_parser/events/object_event'
17
18
  Dir["#{File.dirname(__FILE__)}/tf2_line_parser/events/*.rb"].sort.each { |file| require file }
18
19
  require 'tf2_line_parser/line'
19
20
 
@@ -757,7 +757,7 @@ module TF2LineParser
757
757
  objectowner_uid = '24'
758
758
  objectowner_steam_id = '[U:1:161147993]'
759
759
  objectowner_team = 'Blue'
760
- expect(Events::KilledObject).to receive(:new).with(anything, player_name, player_uid, player_steam_id, player_team, object, weapon, objectowner_name, objectowner_uid, objectowner_steam_id, objectowner_team)
760
+ expect(Events::KilledObject).to receive(:new).with(anything, player_name, player_uid, player_steam_id, player_team, object, weapon, objectowner_name, objectowner_uid, objectowner_steam_id, objectowner_team, nil)
761
761
  parse(line)
762
762
  end
763
763
 
@@ -779,10 +779,183 @@ module TF2LineParser
779
779
  parse(line)
780
780
  end
781
781
 
782
+ it 'recognizes assisted killed object' do
783
+ result = parse('L 06/15/2026 - 19:56:47: "Jib<3><[U:1:111]><Red>" triggered "killedobject" (object "OBJ_SENTRYGUN") (objectowner "Owner<22><[U:1:222]><Blue>") (assist "1") (assister_position "-2268 2938 -255") (attacker_position "-2263 2869 -255")')
784
+ expect(result).to be_a(Events::KilledObject)
785
+ expect(result.player.name).to eq('Jib')
786
+ expect(result.object).to eq('OBJ_SENTRYGUN')
787
+ expect(result.objectowner.steam_id).to eq('[U:1:222]')
788
+ expect(result.weapon).to be_nil
789
+ expect(result.assist).to be(true)
790
+ end
791
+
792
+ it 'does not mark killed object by the killer as assisted' do
793
+ result = parse('L 06/15/2026 - 19:56:47: "Jib<3><[U:1:111]><Red>" triggered "killedobject" (object "OBJ_SENTRYGUN") (weapon "tf_projectile_rocket") (objectowner "Owner<22><[U:1:222]><Blue>") (attacker_position "0 0 0")')
794
+ expect(result.weapon).to eq('tf_projectile_rocket')
795
+ expect(result.assist).to be(false)
796
+ end
797
+
798
+ it 'recognizes team intermission win limit without a reason' do
799
+ result = parse('L 06/15/2026 - 22:07:11: Team "RED" triggered "Intermission_Win_Limit"')
800
+ expect(result).to be_a(Events::IntermissionWinLimit)
801
+ expect(result.team).to eq('RED')
802
+ expect(result.reason).to be_nil
803
+ end
804
+
805
+ it 'recognizes disconnects whose reason continues on the next line' do
806
+ result = parse('L 06/15/2026 - 19:05:27: "Jib<3><[U:1:111]><Red>" disconnected (reason "Client left game (Steam auth ticket has been canceled)' + "\n")
807
+ expect(result).to be_a(Events::Disconnect)
808
+ expect(result.player.name).to eq('Jib')
809
+ expect(result.message).to eq('Client left game (Steam auth ticket has been canceled)')
810
+ end
811
+
812
+ it 'keeps quotes inside a complete disconnect reason' do
813
+ result = parse('L 06/15/2026 - 19:05:27: "Jib<3><[U:1:111]><Red>" disconnected (reason "Kicked "x") by admin")' + "\n")
814
+ expect(result.message).to eq('Kicked "x") by admin')
815
+ end
816
+
817
+ it 'recognizes server cvar changes' do
818
+ result = parse('L 02/22/2026 - 13:42:46: server_cvar: "mp_timelimit" "30"')
819
+ expect(result).to be_a(Events::ServerCvar)
820
+ expect(result.name).to eq('mp_timelimit')
821
+ expect(result.value).to eq('30')
822
+ end
823
+
824
+ it 'recognizes server cvar changes with an empty value' do
825
+ expect(parse('L 02/22/2026 - 13:42:46: server_cvar: "sv_contact" ""').value).to eq('')
826
+ end
827
+
828
+ it 'recognizes picking up a building' do
829
+ result = parse('L 06/15/2026 - 21:53:08: "Jib<3><[U:1:111]><Red>" triggered "player_carryobject" (object "OBJ_SENTRYGUN") (position "-804 663 544")')
830
+ expect(result).to be_a(Events::CarryObject)
831
+ expect(result.player.name).to eq('Jib')
832
+ expect(result.object).to eq('OBJ_SENTRYGUN')
833
+ expect(result.position).to eq('-804 663 544')
834
+ end
835
+
836
+ it 'recognizes dropping a carried building' do
837
+ result = parse('L 06/15/2026 - 21:53:08: "Jib<3><[U:1:111]><Red>" triggered "player_dropobject" (object "OBJ_SENTRYGUN") (position "-804 663 544")')
838
+ expect(result).to be_a(Events::DropObject)
839
+ expect(result.player.name).to eq('Jib')
840
+ expect(result.object).to eq('OBJ_SENTRYGUN')
841
+ expect(result.position).to eq('-804 663 544')
842
+ end
843
+
844
+ it 'recognizes detonating a building' do
845
+ result = parse('L 06/15/2026 - 21:53:08: "Jib<3><[U:1:111]><Red>" triggered "object_detonated" (object "OBJ_SENTRYGUN") (position "-804 663 544")')
846
+ expect(result).to be_a(Events::ObjectDetonated)
847
+ expect(result.player.name).to eq('Jib')
848
+ expect(result.object).to eq('OBJ_SENTRYGUN')
849
+ expect(result.position).to eq('-804 663 544')
850
+ end
851
+
852
+ it 'recognizes steam userid validation' do
853
+ result = parse('L 02/02/2014 - 22:10:19: "A "fake" name<21><[U:1:111]><>" STEAM USERID validated')
854
+ expect(result).to be_a(Events::SteamUseridValidated)
855
+ expect(result.player.name).to eq('A "fake" name')
856
+ expect(result.player.steam_id).to eq('[U:1:111]')
857
+ end
858
+
859
+ it 'recognizes log file started' do
860
+ result = parse('L 02/07/2013 - 21:21:08: Log file started (file "logs/L0207006.log") (game "/home/tf2/tf") (version "5198")')
861
+ expect(result).to be_a(Events::LogFileStarted)
862
+ expect(result.file).to eq('logs/L0207006.log')
863
+ expect(result.game).to eq('/home/tf2/tf')
864
+ expect(result.version).to eq('5198')
865
+ end
866
+
867
+ it 'recognizes log file closed' do
868
+ expect(parse('L 02/22/2026 - 13:42:56: Log file closed.')).to be_a(Events::LogFileClosed)
869
+ end
870
+
871
+ it 'recognizes log file closed without a trailing period' do
872
+ expect(parse('L 02/07/2013 - 21:56:13: Log file closed')).to be_a(Events::LogFileClosed)
873
+ end
874
+
875
+ it 'recognizes loading map' do
876
+ result = parse('L 06/15/2026 - 21:32:52: Loading map "cp_process_f12"')
877
+ expect(result).to be_a(Events::LoadingMap)
878
+ expect(result.map).to eq('cp_process_f12')
879
+ end
880
+
881
+ it 'recognizes started map' do
882
+ result = parse('L 06/15/2026 - 21:32:52: Started map "cp_process_f12" (CRC "df0d50ef")')
883
+ expect(result).to be_a(Events::MapStarted)
884
+ expect(result.map).to eq('cp_process_f12')
885
+ expect(result.crc).to eq('df0d50ef')
886
+ end
887
+
888
+ it 'does not report started map as an unknown event' do
889
+ result = parse('L 06/15/2026 - 21:32:52: Started map "cp_process_f12" (CRC "df0d50ef")')
890
+ expect(result).not_to be_a(Events::Unknown)
891
+ end
892
+
893
+ it 'does not parse chat mentioning a started map as a map start' do
894
+ expect(parse('L 06/15/2026 - 21:32:52: "Jib<3><[U:1:111]><Red>" say "Started map "cp_reset" (CRC "abc")"')).to be_a(Events::Say)
895
+ end
896
+
897
+ it 'recognizes meta data' do
898
+ result = parse('L 06/15/2026 - 21:55:48: World triggered "meta_data" (map "cp_process_f12")')
899
+ expect(result).to be_a(Events::MetaData)
900
+ expect(result.key).to eq('map')
901
+ expect(result.value).to eq('cp_process_f12')
902
+ end
903
+
904
+ it 'recognizes meta data with quotes in the value' do
905
+ result = parse('L 06/15/2026 - 21:55:48: World triggered "meta_data" (title "BLU vs "RED"")')
906
+ expect(result).to be_a(Events::MetaData)
907
+ expect(result.key).to eq('title')
908
+ expect(result.value).to eq('BLU vs "RED"')
909
+ end
910
+
911
+ it 'recognizes round overtime' do
912
+ expect(parse('L 06/15/2026 - 19:33:13: World triggered "Round_Overtime"')).to be_a(Events::RoundOvertime)
913
+ end
914
+
915
+ it 'recognizes game paused' do
916
+ expect(parse('L 06/15/2026 - 19:33:13: World triggered "Game_Paused"')).to be_a(Events::GamePaused)
917
+ end
918
+
919
+ it 'recognizes game unpaused' do
920
+ expect(parse('L 06/15/2026 - 19:33:13: World triggered "Game_Unpaused"')).to be_a(Events::GameUnpaused)
921
+ end
922
+
923
+ it 'recognizes pause length' do
924
+ result = parse('L 06/15/2026 - 19:33:22: World triggered "Pause_Length" (seconds "8.69")')
925
+ expect(result).to be_a(Events::PauseLength)
926
+ expect(result.length).to eq('8.69')
927
+ end
928
+
929
+ it 'recognizes a player pausing the match' do
930
+ result = parse('L 06/15/2026 - 19:33:13: "Jib<3><[U:1:111]><Red>" triggered "matchpause"')
931
+ expect(result).to be_a(Events::MatchPause)
932
+ expect(result.player.steam_id).to eq('[U:1:111]')
933
+ end
934
+
935
+ it 'recognizes a player unpausing the match' do
936
+ result = parse('L 06/15/2026 - 19:33:13: "Jib<3><[U:1:111]><Red>" triggered "matchunpause"')
937
+ expect(result).to be_a(Events::MatchUnpause)
938
+ expect(result.player.steam_id).to eq('[U:1:111]')
939
+ end
940
+
941
+ it 'recognizes name changes' do
942
+ result = parse('L 02/07/2013 - 21:22:10: "Jib<3><[U:1:111]><Red>" changed name to "New "quoted" name"')
943
+ expect(result).to be_a(Events::NameChange)
944
+ expect(result.player.name).to eq('Jib')
945
+ expect(result.player.steam_id).to eq('[U:1:111]')
946
+ expect(result.new_name).to eq('New "quoted" name')
947
+ end
948
+
949
+ it 'does not treat a player whose name contains changed name to as a name change' do
950
+ result = parse('L 02/07/2013 - 21:22:10: "x" changed name to "y<3><[U:1:111]><Red>" STEAM USERID validated')
951
+ expect(result).to be_a(Events::SteamUseridValidated)
952
+ expect(result.player.steam_id).to eq('[U:1:111]')
953
+ end
954
+
782
955
  it 'deals with unknown lines' do
783
- line = log_lines[0]
956
+ line = 'L 02/07/2013 - 21:21:08: VAC secure mode is activated.'
784
957
  time = '02/07/2013 - 21:21:08'
785
- unknown = 'Log file started (file "logs/L0207006.log") (game "/home/hz00112/tf2/orangebox/tf") (version "5198")'
958
+ unknown = 'VAC secure mode is activated.'
786
959
  expect(Events::Unknown).to receive(:new).with(time, unknown)
787
960
  parse(line)
788
961
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tf2_line_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.5
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arie
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-10-17 00:00:00.000000000 Z
10
+ date: 2026-11-01 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: coveralls_reborn
@@ -85,6 +85,7 @@ files:
85
85
  - lib/tf2_line_parser/events/assist.rb
86
86
  - lib/tf2_line_parser/events/built_object.rb
87
87
  - lib/tf2_line_parser/events/capture_block.rb
88
+ - lib/tf2_line_parser/events/carry_object.rb
88
89
  - lib/tf2_line_parser/events/charge_deployed.rb
89
90
  - lib/tf2_line_parser/events/charge_ended.rb
90
91
  - lib/tf2_line_parser/events/charge_ready.rb
@@ -95,26 +96,40 @@ files:
95
96
  - lib/tf2_line_parser/events/damage.rb
96
97
  - lib/tf2_line_parser/events/disconnect.rb
97
98
  - lib/tf2_line_parser/events/domination.rb
99
+ - lib/tf2_line_parser/events/drop_object.rb
98
100
  - lib/tf2_line_parser/events/empty_uber.rb
99
101
  - lib/tf2_line_parser/events/entered_game.rb
100
102
  - lib/tf2_line_parser/events/event.rb
101
103
  - lib/tf2_line_parser/events/feign_death.rb
102
104
  - lib/tf2_line_parser/events/final_score.rb
103
105
  - lib/tf2_line_parser/events/first_heal_after_spawn.rb
106
+ - lib/tf2_line_parser/events/game_paused.rb
107
+ - lib/tf2_line_parser/events/game_unpaused.rb
104
108
  - lib/tf2_line_parser/events/headshot_damage.rb
105
109
  - lib/tf2_line_parser/events/heal.rb
106
110
  - lib/tf2_line_parser/events/intermission_win_limit.rb
107
111
  - lib/tf2_line_parser/events/joined_team.rb
108
112
  - lib/tf2_line_parser/events/kill.rb
109
113
  - lib/tf2_line_parser/events/killed_object.rb
114
+ - lib/tf2_line_parser/events/loading_map.rb
115
+ - lib/tf2_line_parser/events/log_file_closed.rb
116
+ - lib/tf2_line_parser/events/log_file_started.rb
110
117
  - lib/tf2_line_parser/events/lost_uber_advantage.rb
118
+ - lib/tf2_line_parser/events/map_started.rb
111
119
  - lib/tf2_line_parser/events/match_end.rb
120
+ - lib/tf2_line_parser/events/match_pause.rb
121
+ - lib/tf2_line_parser/events/match_unpause.rb
112
122
  - lib/tf2_line_parser/events/medic_death.rb
113
123
  - lib/tf2_line_parser/events/medic_death_ex.rb
124
+ - lib/tf2_line_parser/events/meta_data.rb
114
125
  - lib/tf2_line_parser/events/mini_round_length.rb
115
126
  - lib/tf2_line_parser/events/mini_round_selected.rb
116
127
  - lib/tf2_line_parser/events/mini_round_start.rb
117
128
  - lib/tf2_line_parser/events/mini_round_win.rb
129
+ - lib/tf2_line_parser/events/name_change.rb
130
+ - lib/tf2_line_parser/events/object_detonated.rb
131
+ - lib/tf2_line_parser/events/object_event.rb
132
+ - lib/tf2_line_parser/events/pause_length.rb
118
133
  - lib/tf2_line_parser/events/pickup_item.rb
119
134
  - lib/tf2_line_parser/events/player_action_event.rb
120
135
  - lib/tf2_line_parser/events/player_extinguished.rb
@@ -127,15 +142,18 @@ files:
127
142
  - lib/tf2_line_parser/events/round_event_with_variables.rb
128
143
  - lib/tf2_line_parser/events/round_event_without_variables.rb
129
144
  - lib/tf2_line_parser/events/round_length.rb
145
+ - lib/tf2_line_parser/events/round_overtime.rb
130
146
  - lib/tf2_line_parser/events/round_setup_begin.rb
131
147
  - lib/tf2_line_parser/events/round_setup_end.rb
132
148
  - lib/tf2_line_parser/events/round_stalemate.rb
133
149
  - lib/tf2_line_parser/events/round_start.rb
134
150
  - lib/tf2_line_parser/events/round_win.rb
135
151
  - lib/tf2_line_parser/events/score.rb
152
+ - lib/tf2_line_parser/events/server_cvar.rb
136
153
  - lib/tf2_line_parser/events/shot_fired.rb
137
154
  - lib/tf2_line_parser/events/shot_hit.rb
138
155
  - lib/tf2_line_parser/events/spawn.rb
156
+ - lib/tf2_line_parser/events/steam_userid_validated.rb
139
157
  - lib/tf2_line_parser/events/suicide.rb
140
158
  - lib/tf2_line_parser/events/unknown.rb
141
159
  - lib/tf2_line_parser/events/world_intermission_win_limit.rb