#!/usr/bin/perl -w ###################################################################### # Artemis packet dump totals tool, (C) 2015-2019 Nosey Nick Waterman, # https://noseynick.org/artemis/ # All wrong righted, all rights reserved. Licensed under thev GNU # Affero General Public License v3.0 https://www.gnu.org/licenses/agpl.txt # with Commons Clause https://commonsclause.com/ v1.0 ###################################################################### use strict; # ./parser.pl artemis-capture.xz | totals.pl --objstats > foo0 # do some stuff | sort | uniq -c > foo1 # do some stuff | sort | uniq -c > foo2 # do some stuff | sort | uniq -c > foo3 # cat foo* | totals.pl my (%totals, %stat, $clientConsoles, $keep_stats); my %special = ( '#!/usr/bin/perl' => 1, 'use' => 1, 'require' => 1, '_PROTOVERSION' => 1, ')' => 1, '' => 1, 'NO' => 1, # NO DIFFS 'NOT' => 1, # NOT re-run back to binary ###################################################################### 'Version' => sub { print "$_\n"; }, 'clientConsoles' => sub { $clientConsoles = $_; }, 'GameOverReason' => sub { # clean up leading/trailing/double-spaces and "\0": s/ +/ /g; s/ *\\0 *//; s/ *" */"/g; s/UTF16\("(.*?)"\)// or return; my $type = $1; while (s/UTF16\("(.*?)"\)//) { $totals{"\"$type: $1\""}++ if $1; } }, 'GameOverStats' => sub { $keep_stats = 1; }, 'GameOverStat' => sub { # Fighters start with -12345, and get uglier. skip for now: /GameOverStat\([01], (\d+), UTF16\((".*")\)/ or $keep_stats = 0; return unless $keep_stats; $totals{$2} += $1; }, ); while (<>) { chomp; if (/^\s*(\d+)\s+(.*)/) { # "count and stat" line EG output from "uniq -c" or totals.pl $totals{$2} += $1; } elsif (/^# STAT:\s*(\d+)\s+(\S+) \(min (.*) max (.*)\)$/) { # "# STAT:" lines originate from parser.pl --objstats # Somewhat similar to above but also " N Object.Field (min X max Y)" my ($count, $name, $min, $max) = ($1, $2, $3, $4); $stat{$name} += $count; $stat{"$name._min"} //= $min; $stat{"$name._min"} = $min if $min < $stat{"$name._min"}; $stat{"$name._max"} //= $max; $stat{"$name._max"} = $max if $max > $stat{"$name._max"}; } elsif (/^# STAT:\s*(\d+)\s+(\S+) \((\d+) fully-pop\)$/) { # "# STAT:" line for "N ObjectType (M fully-pop)" $stat{$2} += $1; $stat{"$2._fp"} += $3; } elsif (/^# STAT:\s*(\d+)\s+(.*)$/) { # Other "# STAT:" lines $stat{$2} += $1; } elsif (/^#/) { # skip other comments } elsif (/BAD\W*(\w+)/) { # parser.pl BAD lines $totals{"BAD:$1"}++; } elsif (/(TODO)/) { # parser.pl unhandled packet types TODO $totals{$1}++; } elsif (/^(\s*)(\S?\w*)/) { # other parser.pl lines: my ($sp, $fn) = ($1, $2); if ($special{$fn}) { # a parser.pl line that deserves special treatment next unless ref $special{$fn}; $special{$fn}->(); } if ($sp) { # continuation line $totals{"_$fn"}++; } else { # line representing a new packet $totals{$fn}++; $totals{'PACKETS PARSED'}++; } } else { # act like "uniq -c" for lines without a count $totals{$_}++; } } print "$clientConsoles\n" if $clientConsoles; for (sort {($totals{$b} <=> $totals{$a}) || ($b cmp $a)} keys %totals) { printf "%7d %s\n", $totals{$_}, $_; } for (sort keys %stat) { next unless defined $stat{$_}; my (@com, $val); $val = delete $stat{"$_._min"}; push @com, "min $val" if defined $val; $val = delete $stat{"$_._max"}; push @com, "max $val" if defined $val; $val = delete $stat{"$_._fp"}; push @com, "$val fully-pop" if defined $val; printf "# STAT: %7d %s%s%s\n", $stat{$_}, (/=/ ? ' ' : /\./ ? ' ' : ''), $_, @com ? " (@com)" : ''; }