From d1990aa41fc9dd80f12ec6689fa9188cd78d48b0 Mon Sep 17 00:00:00 2001 From: "tristan.champomier" Date: Wed, 29 Jul 2020 15:31:44 +0200 Subject: [PATCH] Git repository creation. Added initial files --- README | 3 + parser.py | 147 ++++++++++++++++++++++ setup.py | 4 + swtor-parser.php | 171 +++++++++++++++++++++++++ swtor-parser.py | 318 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 643 insertions(+) create mode 100755 README create mode 100755 parser.py create mode 100755 setup.py create mode 100755 swtor-parser.php create mode 100755 swtor-parser.py diff --git a/README b/README new file mode 100755 index 0000000..3aa1562 --- /dev/null +++ b/README @@ -0,0 +1,3 @@ +Swtor combat log parser (work in progress) + +To use: parser.py filename \ No newline at end of file diff --git a/parser.py b/parser.py new file mode 100755 index 0000000..3d7bdb2 --- /dev/null +++ b/parser.py @@ -0,0 +1,147 @@ +# Danial Afzal +# iotasquared@gmail.com +import os +import re +from datetime import datetime +import sys +#[03/16/2012 22:53:45] [@Asdffljklasdfj] [@Asdffljklasdfj] [Unnatural Might {1781509484707840}] [RemoveEffect {836045448945478}: Unnatural Might {1781509484707840}] () +#[date] [src] [target] [ability] [effect] (value) +#src = [foo {id}] or [@player] +# ability = abil {id} +# effect = type:effect +# value = # or # type +# roll? int +class Event: + regex = r'[\[<\(]([^\[<\(\]>\)]*)[\]>\)]' + def __init__(self, x): + #print x + data = re.findall(Event.regex, x) + data.append('') + self.to_string = ' '.join(data) + + date = datetime.strptime(data[0], '%m/%d/%Y %H:%M:%S') + self.date = date + self.source = Entity(data[1]) + self.target = Entity(data[2]) + self.ability = Ability(data[3]) + self.effect = Effect(data[4]) + self.value = Value(data[5]) + self.threat = Threat(data[6]) + +class Entity: + def __init__(self, x): + if not x: + self.name = x + self.player = False + elif x[0] == '@': + self.name = x[1:] + self.player = True + else: + self.name = x + self.player = False + +class Ability: + def __init__(self, x): + self.name = x + +class Effect: + def __init__(self, x): + if not x: + self.type = '' + self.detail = '' + return + foo = x.split(':') + if len(foo) > 1: + self.type = foo[0] + self.detail = foo[1] + else: + self.type = x + self.detail = '' + +class Value: + def __init__(self, x): + if not x: + self.value = 0 + self.type = '' + return + foo = x.replace('*','').split(' ') + self.value = int(foo[0]) + if len(foo) > 1: + self.type = foo[1] + else: + self.type='' + +class Threat: + def __init__(self, x): + if not x: + self.value = 0 + self.type = '' + return + foo = x.replace('*','').split(' ') + self.value = int(foo[0]) + if len(foo) > 1: + self.type = foo[1] + else: + self.type='' + +#date, source, target, ability, effect.type/detail, value.value/type, threat +# TODO: +# multiple sources +# more interesting output, graphs? What about that metric site? Google charts? +# App engine seems fun +# Details for abilities: + # dmg per ability +# Combine multiple entries per ability +def main(): + if len(sys.argv) == 1: + filename = 'argorash.txt' + print 'File not specified, defaulting to ' + filename + else: + filename = sys.argv[1] + file = open(filename, 'r') + events = [] + for line in file.readlines(): + events.append(Event(line)) + + players = set([event.source.name for event in events]) + fights = [] + fight = [] + for event in events: + if 'EnterCombat' in event.effect.detail: + fight = [] + if 'ExitCombat' in event.effect.detail: + fight.append(event) + fights.append(fight) + fight = [] + continue + fight.append(event) + + for i, fight in enumerate(fights): + duration = fight[-1].date - fight[0].date + print '\nCombat Statistics for Fight # %d (duration: %s)' %(i+1, str(duration)) + print str(fight[0].date) +" -> " + str(fight[-1].date) + for player in players: + dmg = 0 + heal = 0 + threat = 0 + for event in fight: + if event.source.name == player and 'Damage ' in event.effect.detail: + dmg += event.value.value + if event.source.name == player and event.threat.value: + threat += event.threat.value + if event.source.name == player and 'Heal ' in event.effect.detail: + heal += event.value.value + secs = duration.seconds + if not secs: + secs = 1 + if dmg: + print '%s did %d damage in %s (%d DPS)' % (player, dmg, str(duration), dmg/secs) + if heal: + print '%s did %d heals in %s (%d HPS)' % (player, heal, str(duration), heal/secs) + if threat: + print '%s did %d threat in %s (%d TPS)' % (player, threat, str(duration), threat/secs) + + + #print "\n".join([event.to_string for event in events]) +if __name__ == "__main__": + main() diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..6d4941d --- /dev/null +++ b/setup.py @@ -0,0 +1,4 @@ +from distutils.core import setup +import py2exe + +setup(console=['parser.py']) diff --git a/swtor-parser.php b/swtor-parser.php new file mode 100755 index 0000000..d570445 --- /dev/null +++ b/swtor-parser.php @@ -0,0 +1,171 @@ +#!/usr/bin/php + +'; + $this->fileName = ""; + $this->handle = null; + } + + // Destructor + public function __destruct(){ +// echo 'The class "' . __CLASS__ . '" was destroyed.
'; + } + + // Method to get the filename + public function getFileName(){ + return $this->fileName; + } + + // Method to set the filename + public function setFileName($filename){ + $this->fileName = $filename; + } + + // Method to get the handle content + public function getHandle(){ +// TODO Check file exists + $this->handle = fopen($this->fileName, "r"); +// if($this->handle) { +// while (($line = fgets($this->handle)) !== false) { +// echo $line; +// } +// fclose($this->handle); +// } else { +// $this->handle = False; +// } + + return $this->handle; + } + } + +// class Event: +// regex = r'[\[<\(]([^\[<\(\]>\)]*)[\]>\)]' +// def __init__(self, x): +// data = re.findall(Event.regex, x) +// data.append('') +// self.to_string = ' '.join(data) +// +// dateandtime = datetime.strptime(list(sys.argv[1].split("_"))[1]+" "+data[0], '%Y-%m-%d %H:%M:%S.%f') +// +// self.date = dateandtime +// self.source = Entity(data[1]) +// self.target = Entity(data[2]) +// self.ability = Ability(data[3]) +// self.effect = Effect(data[4]) +// self.value = Value(data[5]) +// self.threat = Threat(data[6]) + + class LogEvent + { + // Properties declaration + public $event = ""; +// public $date = ""; +// public $source = ""; +// public $target = ""; +// public $ability = ""; +// public $effect = ""; +// public $value = 0; +// public $threat = 0; + + // Constructor + public function __construct() + { +// echo 'The class "' . __CLASS__ . '" was initiated!
'; + $this->event = ""; +// $this->date = $date; +// $this->source = $source; +// $this->target = $target; +// $this->ability = $ability; +// $this->effect = $effect; +// $this->value = $value; +// $this->threat = $threat; + } + + // Destructor + public function __destruct(){ +// echo 'The class "' . __CLASS__ . '" was destroyed.
'; + } + + // Method to set the event + public function arrayEvent($logEvent){ +// [23:40:42.150] [@Comendatore] [Combat Training Target MK-10 {2816257300627456}:133000004817] [Ion Pulse {2199843594305536}] [ApplyEffect {836045448945477}: Damage {836045448945501}] (7659* elemental {836045448940875}) <19146> +// [date] [src] [target] [ability] [effect] (value) + $isMatching = preg_match_all('/[\[<\(]([^\[<\(\]>\)]*)[\]>\)]/m', $logEvent, $this->event, PREG_SET_ORDER, 0); + return $this->event; + } + } + +// $handle = fopen("combat_2020-06-11_18_06_42_493608.txt", "r"); + $handlecontent = new loadFile; + $handlecontent->setFileName("combat_2020-07-25_22_39_58_532658.txt"); + $handleresult = $handlecontent->getHandle(); + + if($handleresult) { + $totalAmount=0;$totalThreat=0; + while (($line = fgets($handleresult)) !== false) { + $log = new LogEvent; + $log = $log->arrayEvent($line); + if( array_key_exists(6, $log) ); +// print_r($log); + $logDate = $log[0][1]; + $logSource = $log[1][1]; + $logTarget = $log[2][1]; + $logAbility = $log[3][1]; + $logEffect = $log[4][1]; + $logValue = (isset($log[5][1]) && ($log[5][1] != 0))?$log[5][1]:null; + $logThreat = isset($log[6][1])?$log[6][1]:null; + print("Date : ".$logDate." Source : ".$logSource." Target : ".$logTarget." Ability : ".$logAbility." Effect : ".$logEffect." Amount : ".$logValue." Menace : ".$logThreat."\n"); + +// if( $log[1][1] + + $totalAmount+=$logValue;$totalThreat+=$logThreat; + } + print("Total Damages : ".$totalAmount."\n"); + print("Total Menace : ".$totalThreat."\n"); + } else { + echo "Error while loading file handle !"; + } +?> diff --git a/swtor-parser.py b/swtor-parser.py new file mode 100755 index 0000000..1cd9549 --- /dev/null +++ b/swtor-parser.py @@ -0,0 +1,318 @@ +#!/usr/bin/python + +import os, re, sys +from time import strftime +from datetime import datetime + +#import matplotlib.pyplot as plt + +## Declaring the points for first line plot +#X1 = [1,2,3,4,5] +#Y1 = [2,4,6,8,10] + +## Setting the figure size +#fig = plt.figure(figsize=(10,5)) + +## plotting the first plot +#plt.plot(X1, Y1, label = "plot 1") + +## Declaring the points for second line plot +#X2 = [1,2,3,4,5] +#Y2 = [1,4,9,16,25] + +## plotting the second plot +#plt.plot(X2, Y2, label = "plot 2") + +## Labeling the X-axis +#plt.xlabel('X-axis') + +## Labeling the Y-axis +#plt.ylabel('Y-axis') + +## Give a title to the graph +#plt.title('Two plots on the same graph') + +## Show a legend on the plot +#plt.legend() + +##Saving the plot as an image +#fig.savefig('line plot.png', bbox_inches='tight', dpi=150) + +##Showing the plot +#plt.show() + + + + +# [03/16/2012 22:53:45] [@Asdffljklasdfj] [@Asdffljklasdfj] [Unnatural Might {1781509484707840}] [RemoveEffect {836045448945478}: Unnatural Might {1781509484707840}] () +# [21:18:16.172] [@Comendatore] [@Comendatore] [] [Event {836045448945472}: EnterCombat {836045448945489}] (Asation (8 Player Story)) +#[date] [src] [target] [ability] [effect] (value) +#src = [foo {id}] or [@player] +# ability = abil {id} +# effect = type:effect +# value = # or # type +# roll? int + +listEncounters = [ + ## The Dread Fortress + 'Nefra, Who Bars the Way {3266533082005504}','Gate Commander Draxus {3273924720721920}','Grob\'thok, Who Feeds the Forge {3273929015689216}','Corruptor Zero {3273933310656512}','Dread Master Brontes {3273937605623808}', + ## Eternity Vault + 'Soa {2289823159156736}', + ## + 'The Writhing Horror {2938874321960960}','Heirad {2938002443599872}','Ciphas {2938011033534464}','Kel\'sara {2938006738567168}','Operator IX {2942606648541184}','Kephess the Undying {2937620191510528}','The Terror From Beyond {2978340776443904}','The Terror From Beyond {3025233229381632}', + ## + 'The Eyeless {3328380611067904}','Golden Fury {3232800408862720}','Golden Fury {3232817588731904}', + ## The False Emperor + 'Tregg the Destroyer {1690314444111872}','Jindo Krey {770959514533888}','HK-47 {770955219566592}','Chondrus Berani {1690331623981056}','Sith Entity {1695962326106112}','Forsaken Sith Lord {781830076760064}','Darth Malgus {770963809501184}', + ## Battle of Rishi + 'Marko Ka {3533564083699712}','Rarrook {3533559788732416}','Lord Vodd {3533585558536192}','Shield Squadron Unit 1 {3533594148470784}','Darth Yun {4257506591244288}', + ## Objective Meridian + 'Commander Aster {4258021987319808}','Darth Malgus {4257502296276992}', + ## Heart of Ruin + 'Colossal Monolith {3541140406009856}', + ## + 'Toxxun {3340011382505472}', + ## Mutated Geonosian Queen + 'Mutated Geonosian Queen {4197299739688960}', + ## + 'SCYVA {4108140513591296}' + ] + +dmgTypes = ['energy','elemental','kinetic','internal'] +instTypes = ['Story','Veteran','Master'] +zoneNames = [ + # Planet zones + 'Corellia','Coruscant','CZ-198','Rishi','Taris','Tatooine','Voss','Yavin 4','Ziost', + # Instances zones + 'Asation','Lair of the Eyeless','Toborro\'s Palace Courtyard','The False Emperor','Battle of Rishi','Objective Meridian','Hive of the Mountain Queen', + # Operations Zones + 'Valley of the Machine Gods' + ] + +class Event: + regex = r'[\[<\(]([^\[<\(\]>\)]*)[\]>\)]' + def __init__(self, x): + data = re.findall(Event.regex, x) + data.append('') + self.to_string = ' '.join(data) + + dateandtime = datetime.strptime(list(sys.argv[1].split("_"))[1]+" "+data[0], '%Y-%m-%d %H:%M:%S.%f') + + self.date = dateandtime + self.source = Entity(data[1]) + self.target = Entity(data[2]) + self.ability = Ability(data[3]) + self.effect = Effect(data[4]) + self.value = Value(data[5]) + self.threat = Threat(data[6]) + +class Entity: + def __init__(self, x): + if not x: + self.name = x + self.player = False + #print(x+" is not a player !") + elif x[0] == '@': + self.name = x[1:] + self.player = True + #print(x+" is a player !") + else: + self.name = x + self.player = False + #print(x+" is not a player !") + +class Ability: + def __init__(self, x): + self.name = x + +class Effect: + def __init__(self, x): + if not x: + self.type = '' + self.detail = '' + return + foo = x.split(':') + if len(foo) > 1: + self.type = foo[0] + self.detail = foo[1] + else: + self.type = x + self.detail = '' + +#class Value: + #def __init__(self, x): + #if not x: + #self.value = 0 + #self.type = '' + #return + #foo = x.replace('*','').split(' ') + #self.value = int(foo[0]) + #if len(foo) > 1: + #self.type = foo[1] + #else: + #self.type='' + +# [20:06:53.053] [@Comendatore] [@Comendatore] [] [Event {836045448945472}: EnterCombat {836045448945489}] (Ziost) +# [21:31:45.974] [@Comendatore] [@Comendatore] [] [Event {836045448945472}: EnterCombat {836045448945489}] (Hive of the Mountain Queen (8 Player Story)) +# [21:32:18.105] [@Bidobulle] [@Comendatore] [Slow-release Medpac {3406415871868928}] [ApplyEffect {836045448945477}: Heal {836045448945500}] (5683*) +# [21:32:19.499] [@Bidobulle] [@Comendatore] [Slow-release Medpac {3406415871868928}] [ApplyEffect {836045448945477}: Heal {836045448945500}] (3641) +# [22:37:11.036] [@Comendatore] [Colossal Monolith {3541140406009856}:15814000004760] [Shocked {2044520397013249}] [ApplyEffect {836045448945477}: Damage {836045448945501}] (165 energy {836045448940874}) <1646> +# [22:37:46.168] [@Comendatore] [Colossal Monolith {3541140406009856}:15814000004760] [Ion Storm {3389987621961728}] [ApplyEffect {836045448945477}: Damage {836045448945501}] (20879* elemental {836045448940875}) <52197> +# [22:37:51.623] [Colossal Monolith {3541140406009856}:15814000004760] [@Comendatore] [Great Slam {3543287889657856}] [ApplyEffect {836045448945477}: Damage {836045448945501}] (86 kinetic {836045448940873} -shield {836045448945509} (86 absorbed {836045448945511})) <86> +# [22:38:30.231] [Colossal Monolith {3541140406009856}:15814000004760] [@Comendatore] [Bleeding {3546045258662192}] [ApplyEffect {836045448945477}: Damage {836045448945501}] (10668 internal {836045448940876}) <10668> + +class Value: + def __init__(self, x): + if not x: + self.value = 0 + self.type = '' + return + foo = x.replace('*','') + #print(foo) + #if not re.search('[a-zA-Z]+', x): + #print(foo) + if any(z in foo for z in dmgTypes) and not any(y in foo for y in instTypes) or not any(w in foo for w in zoneNames) or foo.isdigit(): + foo = x.replace('*','').split(' ') + if foo[0].isdigit(): + self.value = int(foo[0]) + else: + self.value = 0 + if len(foo) > 1: + self.type = foo[1] + else: + self.type='' + else: + #print(foo) + self.type = '' + +class Threat: + def __init__(self, x): + if not x: + self.value = 0 + self.type = '' + return + foo = x.replace('*','').split(' ') + self.value = int(foo[0]) + if len(foo) > 1: + self.type = foo[1] + else: + self.type='' + +#date, source, target, ability, effect.type/detail, value.value/type, threat +# TODO: +# multiple sources +# more interesting output, graphs? What about that metric site? Google charts? +# App engine seems fun +# Details for abilities: + # dmg per ability +# Combine multiple entries per ability +def main(): + if len(sys.argv) == 1: + filename = 'combat_2020-07-25_22_39_58_532658.txt' + print('File not specified, defaulting to ' + filename) + else: + filename = sys.argv[1] + file = open(filename, 'r') + events = [] + for line in file.readlines(): + events.append(Event(line)) + + players = set([event.source.name for event in events]) + fights = [] + isBoss = False + fight = [] + for event in events: + if 'EnterCombat' in event.effect.detail: + fight = [] + #fight.extend(fight) + #isBoss = False + + if 'Death' in event.effect.detail: + #print(event.effect.detail) + #if any(t in event.target.name for t in listEncounters): + fight.append(event) + #print(listEncounters) + #isBoss = False + for event in fight: + ##print(event.source.name) + if any(t in event.target.name for t in listEncounters): + isBoss = True + if isBoss: + fights.append(fight) + fight = [] + continue + + if 'ExitCombat' in event.effect.detail: + #if any(t in event.effect.detail for t in ['ExitCombat','Death']): + fight.append(event) + #print(listEncounters) + isBoss = False + for event in fight: + #print(event.source.name) + if any(t in event.target.name for t in listEncounters): + isBoss = True + if isBoss: + fights.append(fight) + fight = [] + continue + + fight.append(event) + + for i, fight in enumerate(fights): + duration = fight[-1].date - fight[0].date + for event in fight: + if any(t in event.target.name for t in listEncounters): + namedTarget = event.target.name.split(' {') + namedTarget = namedTarget[0] + #print('\nCombat Statistics for Fight # %d (duration: %s)' %(i+1, str(duration))) + print('\nCombat Statistics for Fight #%d -> %s (%s)' %(i+1, namedTarget, str(duration))) + print("Started: " + str(fight[0].date) +" Ended: " + str(fight[-1].date)) + for player in players: + if '{' not in player: + dmg = 0 + tdmg = 0 + heal = 0 + theal = 0 + threat = 0 + death = False + for event in fight: + ## Domages done by player (DPS) + if event.source.name == player and 'Damage ' in event.effect.detail: + dmg += event.value.value + ## Domages taken by player (DTPS) + if event.target.name == player and 'Damage ' in event.effect.detail: + tdmg += event.value.value + ## Heal done by player (HPS) + if event.source.name == player and 'Heal ' in event.effect.detail: + heal += event.value.value + ## Heal received by player (HTPS) + if event.target.name == player and 'Heal ' in event.effect.detail: + theal += event.value.value + ## Generated threat by player (TPS) + if event.source.name == player and event.threat.value: + threat += event.threat.value + ## Dead + if event.source.name == player and 'Death' in event.effect.detail: + death = True + + secs = duration.seconds + + if not secs: + secs = 1 + if dmg: + print('%s did %d damage in %s (%d DPS)' % (player, dmg, str(duration), dmg/secs)) + if tdmg: + print('%s took %d damage in %s (%d DTPS)' % (player, tdmg, str(duration), tdmg/secs)) + if heal: + print('%s did %d heals in %s (%d HPS)' % (player, heal, str(duration), heal/secs)) + if theal: + print('%s received %d heals in %s (%d HTPS)' % (player, theal, str(duration), theal/secs)) + if threat: + print('%s did %d threat in %s (%d TPS)' % (player, threat, str(duration), threat/secs)) + if death: + print('%s died on %s' % (player, namedTarget)) + + + #print("\n".join([event.to_string for event in events])) +if __name__ == "__main__": + main()