diff --git a/.git_update.ps1 b/.git_update.ps1 new file mode 100644 index 0000000..4af2ec2 --- /dev/null +++ b/.git_update.ps1 @@ -0,0 +1,8 @@ +$backupDir = New-Item ".\.backup_$('{0:yyyy-MM-dd-hh-mm-ss}' -f (Get-Date))" -ItemType Directory + +foreach ($f in @('FO_stats_v2.ps1','FO_stats_join-json.ps1','_FoDownloader.ps1','.fo_stats.css')) { + +Copy-Item $f $backupDir +(Invoke-WebRequest https://raw.githubusercontent.com/haze-au/fo-stats/main/$f -DisableKeepAlive -Headers @{"Cache-Control"="no-cache"}).Content | Out-File $f + +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc8f1ed --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +.old +_daily +melbourne +california +sydney +dallas +virginia +cert +ireland +nz +mumbai +_FoDownloader_backup.ps1 +dist/sorts/tablesort.date.min.js +test.html +test.json +test4.html +test.js +test4.html +test4.html +2023-03-20-10-31-31_\[ff-raiden\]_blue_vs_red.html +2023-03-20-10-31-31_\[ff-raiden\]_blue_vs_red.json +2023-03-20-10-31-31_\[ff-raiden\]_blue_vs_red_stats.json +test.css +ts.js +*.html +*.json + +._daily_refresher.ps1 +restore.zip +.restore_batches.ps1 diff --git a/FO_Stats_with_downloader_R2c_SelfSigned.zip b/FO_Stats_with_downloader_R2c_SelfSigned.zip new file mode 100644 index 0000000..b08ff4a Binary files /dev/null and b/FO_Stats_with_downloader_R2c_SelfSigned.zip differ diff --git a/FO_stats_join-json.ps1 b/FO_stats_join-json.ps1 new file mode 100644 index 0000000..e59ef68 --- /dev/null +++ b/FO_stats_join-json.ps1 @@ -0,0 +1,636 @@ +## _daily/US/FOSummary*_yyyy-MM-dd.txt +# _daily/US/.batch +# _daily/US/.new +### + +param([switch]$ForceBatch, + [string]$RemoveMatch, # JSON file to remove $FromJson file + [string]$FromJson, # See Remove Match + [string]$StartDateTime, # Input UTC time to match file names + [int]$StartOffsetDays, # If not StartDateTime use End Days/hours + [int]$StartOffsetHours, + [string]$EndDateTime, # Input UTC time to match file names + [string]$FilterPath, + [ValidateSet('ALL','US','BR','EU','OCE','INT')] + $Region, + [switch]$AllowUnranked, + [string]$PlayerCount, + [string]$GenerateHTML, + [string]$ExcludeFile, + [string]$OutFile ) + +if ($ForceBatch) { $doBatch = $true } +if (!$PlayerCount) { $PlayerCount = '([4-9]|[1-2][0-9])' } + +# Update me for -Region parameter and Daily Stats updates +$OCEPaths = @('sydney/','melbourne/') +$USPaths = @('california/','dallas/','virginia/','miami/','phoenix/','atlanta/') +$BRPaths = @('saopaulo/','fortaleza/') +$EUPaths = @('ireland/','stockholm/','london/') +$IntPaths = @('bahrain/','guam/','mumbai/','tokyo/') + +$script:ClassToStr = @('World','Sco','Snp','Sold','Demo','Med','HwG','Pyro','Spy','Eng', 'SG') +$script:ClassAllowedStr = @('Sco','Sold','Demo','Med','HwG','Pyro','Spy','Eng') +$script:ClassAllowed = @(1,3,4,5,6,7,8,9) +$script:ClassAllowedWithSG = @(1,3,4,5,6,7,8,9,10) + +$regExReplaceFix = '[[+*?()\\.]', '\$&' + +function Format-MinSec { + param($sec) + + if ($sec -eq 0) { return '' } + $ts = New-TimeSpan -Seconds $sec + $mins = ($ts.Days * 24 + $ts.Hours) * 60 + $ts.minutes + return "$($mins):$("{0:d2}" -f $ts.Seconds)" +} + +function Sum-MinSec { + param($minSec1,$minSec2,[switch]$Deduct) + + $split1 = $minSec1 -split ':' + $split2 = $minSec2 -split ':' + + if ($Deduct) { $removeModifier = -1 } + else { $removeModifier = 1 } + + $secs = (([int]$split1[0] + ($removeModifier * [int]$split2[0])) * 60) + ([int]$split1[1] + ($removeModifier * [int]$split2[1]) ) + + return (Format-MinSec $secs) +} + +function Table-ClassInfo { + param([ref]$Table,$Name,$TimePlayed) + $out = '' + $classlist = @{} + + $timePlayedSplit = $TimePlayed -split ':' + $timePlayedSecs = ([int]$timePlayedSplit[0] * 60) + [int]$timePlayedSplit[1] + + foreach ($p in $Table.Value) { + if ($p.Name -eq $Name) { + foreach ($class in $ClassAllowed) { + $strClass = $ClassToStr[$class] + $time = $p.($strClass) + if ($time -notin 0,'',$null) { + $classlist.$strClass = ($time / $timePlayedSecs) + } + } + + foreach ($c in ($classlist.GetEnumerator() | Sort-Object Value -Descending)) { + $out += "$(($c.Name).PadRight(4)) $(('{0:P0}' -f $c.Value).PadLeft(3))|" + } + + return $out -replace '\|$','' + } + } +} + +function arrFindPlayer { + param([ref]$Array,$Name) + $i = 0 + foreach ($p in $array.Value) { + if ($p.Name -eq $Name) { return $i } + $i++ + } + + return -1 +} + + +function arrFindPlayer-Class { + param([ref]$Array,$Name) + $i = 0 + foreach ($p in $Array.Value) { + if ($p.Name -eq $Name) { return $i } + $i++ + } + + return -1 +} + + +function processFoStatsJSON { + param( [Parameter(Mandatory=$true)]$CurrentJson, + [Parameter(Mandatory=$true)]$NewJson, + [switch]$RemoveMatch + ) + + $removeModifier = 1 + if ($RemoveMatch) { + $CurrentJson.Matches = ($CurrentJson.Matches | Where-Object Match -ne $NewJson.Matches[0].Match) + $removeModifier = -1 + } else { + $CurrentJson.Matches += $NewJson.Matches + } + + + + #Add/Minus the JSONs + foreach ($array in @('SummaryAttack','SummaryDefence')) { + foreach ($p in $NewJson.$array) { + $pos = (arrFindPlayer ([ref]$CurrentJson.$array) $p.Name) + if ($removeModifier -gt 0 -and $pos -lt 0) { + [array]$CurrentJson.$array += [PSCustomObject]@{ + Name = $p.Name + KPM = $null + KD = $null + Kills = 0 + Death = 0 + TKill = 0 + Dmg = 0 + DPM = $null + SGKills = 0 + SGDeath = 0 + FlagCap = 0 + FlagTake = 0 + FlagTime = 0 + FlagStop = 0 + Win = 0 + Draw = 0 + Loss = 0 + TimePlayed = 0 + Classes = '' + } + $pos = ($CurrentJson.$array.Length) - 1 + } + + $CurrentJson.$array[$pos].Kills += $p.Kills * $removeModifier + $CurrentJson.$array[$pos].Death += $p.Death * $removeModifier + $CurrentJson.$array[$pos].TKill += $p.TKill * $removeModifier + $CurrentJson.$array[$pos].Dmg += $p.Dmg * $removeModifier + $CurrentJson.$array[$pos].FlagStop += $p.FlagStop * $removeModifier + $CurrentJson.$array[$pos].FlagTake += $p.FlagTake * $removeModifier + $CurrentJson.$array[$pos].FlagCap += $p.FlagCap * $removeModifier + $CurrentJson.$array[$pos].SGKills += $p.SGKills * $removeModifier + $CurrentJson.$array[$pos].SGDeath += $p.SGDeath * $removeModifier + $CurrentJson.$array[$pos].Win += $p.Win * $removeModifier + $CurrentJson.$array[$pos].Loss += $p.Loss * $removeModifier + $CurrentJson.$array[$pos].Draw += $p.Draw * $removeModifier + + if ($RemoveMatch) { + $CurrentJson.$array[$pos].TimePlayed = Sum-MinSec -MinSec1 $CurrentJson.$array[$pos].TimePlayed -MinSec2 ($p.TimePlayed) -Deduct + $CurrentJson.$array[$pos].FlagTime = Sum-MinSec -MinSec1 $CurrentJson.$array[$pos].FlagTime -MinSec2 ($p.FlagTime) -Deduct + } else { + $CurrentJson.$array[$pos].TimePlayed = Sum-MinSec $CurrentJson.$array[$pos].TimePlayed ($p.TimePlayed) + $CurrentJson.$array[$pos].FlagTime = Sum-MinSec $CurrentJson.$array[$pos].FlagTime ($p.FlagTime) + } + + if ($CurrentJson.$array[$pos].TimePlayed -in '0:00','') { + $CurrentJson.$array = $CurrentJson.$array | Where-Object Name -ne $p.Name + } + + } + } + + foreach ($strTable in @("ClassFragAttack","ClassFragDefence","ClassTimeAttack","ClassTimeDefence")) { + foreach ($p in $NewJson.$strTable) { + $pos = (arrFindPlayer-Class ([ref]$CurrentJson.$strTable) $p.Name) + if ($removeModifier -gt 0 -and $pos -lt 0) { + [array]$CurrentJson.$strTable += [PSCustomObject]@{ + Name = $p.Name + Sco = 0 + KPM1 = $p.KPM1 + Sold = 0 + KPM3 = $p.KPM3 + Demo = 0 + KPM4 = $p.KPM4 + Med = 0 + KPM5 = $p.KPM5 + HwG = 0 + KPM6 = $p.KPM6 + Pyro = 0 + KPM7 = $p.KPM7 + Spy = 0 + KPM8 = $p.KPM8 + Eng = 0 + KPM9 = $p.KPM9 + SG = 0 + KPM0 = $p.KPM0 + } + $pos = ($CurrentJson.$strTable.Length) - 1 + } + + foreach ($classID in $ClassAllowedWithSG) { + if ($classID -eq 10 -and $strTable -like 'ClassTime*') { continue } + $class = $ClassToStr[$classID] + $CurrentJson.$strTable[$pos].$class += $p.$class * $removeModifier + } + + if (($CurrentJson.$strTable[$pos] | Measure-Object sco,sold,demo,med,hwg,pyro,spy,eng -Sum | foreach { $_.Sum } | Where { $_ -gt 0 }).Count -lt 1) { + $CurrentJson.$strTable = $CurrentJson.$strTable | Where-Object Name -ne $p.Name + } + } + } + + + #Recalcuted stats - i.e KD, per-min + $x = 1 + foreach ($table in @($CurrentJson.SummaryAttack,$CurrentJson.SummaryDefence)) { + if ($x -eq 1) { $classTable = [ref]$CurrentJson.ClassTimeAttack } + else { $classTable = [ref]$CurrentJson.ClassTimeDefence } + + foreach ($player in $table) { + $timePlayed = $player.TimePlayed -split ':' + $timeMins = [double]$timeplayed[0] + ([double]$timePlayed[1] / 60) + $player.KPM = '{0:0.00}' -f ($player.Kills / $timeMins) + $player.KD = '{0:0.00}' -f ($player.Kills / $player.Death) + $player.DPM = '{0:0}' -f ($player.Dmg / $timeMins) + $player.Classes = (Table-ClassInfo ($classTable) $player.Name $player.TimePlayed) + } + $x++ + } + + $x = 1 + foreach ($table in @($CurrentJson.ClassFragAttack,$CurrentJson.ClassFragDefence)) { + foreach ($player in $table) { + if ($x -eq 1) { $classTable = [ref]$CurrentJson.ClassTimeAttack } + else { $classTable = [ref]$CurrentJson.ClassTimeDefence } + + foreach ($classID in $ClassAllowedWithSG) { + $class = $ClassToStr[$classID] + $classTime = ($ClassTable.Value | Where Name -EQ $player.Name)."$(if ($classID -eq 10) { "Eng" } else { $class })" /60 + + if ($classTime -gt 0 -and $player.$class -gt 0) { + $player."KPM$(if ($classID -eq 10) { 0 } else { $classID })" = ('{0:0.00}' -f ($player.$class / $classTime)) + } else { $player."KPM$(if ($classID -eq 10) { 0 } else { $classID })" = $null } + } + } + $x++ + } + return $CurrentJson +} + +function Generate-DailyStatsHTML { + param([array]$JSON) + + $htmlBody = '

Match Log

' + #if (($JSON.Matches.Server -ne $null).Count -gt 0) { + # $htmlBody += ($JSON.Matches | Sort-Object Match | ConvertTo-Html -Fragment Server,Match,Winner,Rating,Score1,Team1,Score2,Team2) -replace '','
' -replace '','' + #} else { + $htmlBody += ($JSON.Matches | Sort-Object Match | ConvertTo-Html -Fragment) -replace '
','
' -replace '
','
' -replace '','' + #} + $htmlBody += '

Attack Summary

' + $htmlBody += ($JSON.SummaryAttack | Select-Object Name,KPM,KD,Kills,Death,TKill,Dmg,SGKills,DPM,FlagCap,FlagTake,FlagTime,Win,Draw,Loss,TimePlayed,Classes | Sort-Object Name | ConvertTo-Html -Fragment) -replace '
','
' -replace '
','
' -replace '','' + $htmlBody += '

Defence Summary

' + $htmlBody += ($JSON.SummaryDefence | Select-Object Name,KPM,KD,Kills,Death,TKill,Dmg,DPM,SGDeath,FlagStop,Win,Draw,Loss,TimePlayed,Classes | Sort-Object Name | ConvertTo-Html -Fragment) -replace '
','
' -replace '
','
' -replace '','' + $htmlBody += '

Class Kills - Attack

' + $htmlBody += ($JSON.ClassFragAttack | Sort-Object Name | ConvertTo-Html -Fragment) -replace '
','
' -replace '
','
' -replace '','' + $htmlBody += '

Class Kills - Defence

' + $htmlBody += ($JSON.ClassFragDefence | Sort-Object Name | ConvertTo-Html -Fragment) -replace '
','
' -replace '
','
' -replace '','' + $htmlBody += '
' + $htmlBody += '

Class Time - Attack

' + $htmlBody += ($JSON.ClassTimeAttack | Select-Object Name, ` + @{L='Sco' ; E={Format-MinSec $_.Sco}}, ` + @{L='Sold'; E={Format-MinSec $_.Sold}}, ` + @{L='Demo'; E={Format-MinSec $_.Demo}}, ` + @{L='Med' ; E={Format-MinSec $_.Med}}, ` + @{L='HwG' ; E={Format-MinSec $_.HwG}}, ` + @{L='Pyro'; E={Format-MinSec $_.Pyro}}, ` + @{L='Spy' ; E={Format-MinSec $_.Spy}}, ` + @{L='Eng' ; E={Format-MinSec $_.Eng}} | Sort-Object Name | ConvertTo-Html -Fragment) -replace '
','
' -replace '
','
' -replace '','' + $htmlBody += '
' + $htmlBody += '

Class Time - Defence

' + $htmlBody += ($JSON.ClassTimeDefence | Select-Object Name, ` + @{L='Sco' ; E={Format-MinSec $_.Sco}}, ` + @{L='Sold'; E={Format-MinSec $_.Sold}}, ` + @{L='Demo'; E={Format-MinSec $_.Demo}}, ` + @{L='Med' ; E={Format-MinSec $_.Med}}, ` + @{L='HwG' ; E={Format-MinSec $_.HwG}}, ` + @{L='Pyro'; E={Format-MinSec $_.Pyro}}, ` + @{L='Spy' ; E={Format-MinSec $_.Spy}}, ` + @{L='Eng' ; E={Format-MinSec $_.Eng}} | Sort-Object Name | ConvertTo-Html -Fragment) -replace '
','
' -replace '
','
' -replace '','' + $htmlBoyd += '' + + $htmlHeader = @" + + + + + + + + +"@ + $htmlPost += '' + + return (ConvertTo-Html -Body $htmlBody -Head $htmlHeader -PostContent $htmlPost) +} # end Generate HTML + +function GetPathFromFileName { + param( $fileName ) + + $arrPath = $fileName -split '[\\/]' + + if ($fileName -match 'brazil[\\/]fo[\\/]upload[\\/]') { + return $arrPath[-4] + '/' + $arrPath[-3] + '/' + $arrPath[-2] + '/' + } else { + return $arrPath[-3] + '/' + $arrPath[-2] + '/' + } +} + +Write-Host " FO_stats_join-json | $(get-date)" +Write-Host '--------------------------------------------' + +if ($GenerateHTML) { + Generate-DailyStatsHTML -JSON (Get-Content $GenerateHTML -Raw | ConvertFrom-Json) | Out-File ($GenerateHTML -replace '.json$','.html') + write-host "Generated HTML:- $GenerateHTML" + return +} + +if ($RemoveMatch) { + if (!$FromJson) { Write-Host '-FromJson required'; return} + + $keepJson = ((Get-Content -LiteralPath $FromJson -Raw) | ConvertFrom-Json) + $remJson = ((Get-Content -LiteralPath $RemoveMatch -Raw) | ConvertFrom-Json) + if ($remJson.Matches.Match -notin $keepJson.Matches.Match) { Write-Host "ERROR: $($remJson.Matches.Match) not found in $FromJson"; return } + + $outJson = (processFoStatsJSON -CurrentJson ($keepJson) -NewJson ($remJson) -RemoveMatch) + + if ($keepJson -eq $null -or $remJson -eq $null) { Write-Host "NULL JSON ERROR"; return } + ($outJson | ConvertTo-Json) | Out-File -LiteralPath $FromJson -Encoding utf8 + Generate-DailyStatsHTML -JSON $outJson | Out-File -LiteralPath ($FromJson -replace '\.json$','.html') -Encoding utf8 + Write-Host "Removed: $RemoveMatch" + return +} + +if ($Region -and $FilterPath -notmatch '[.]json$') { + if ($Region -eq 'ALL') { $LatestPaths = $OCEPaths + $USPaths + $EUPaths + $IntPaths } + elseif ($Region -eq 'US') { $LatestPaths = $USPaths } + elseif ($Region -eq 'BR') { $LatestPaths = $BRPaths } + elseif ($Region -eq 'EU') { $LatestPaths = $EUPaths } + elseif ($Region -eq 'OCE') { $LatestPaths = $OCEPaths } + elseif ($Region -eq 'INT') { $LatestPaths = $IntPaths } + + if ($FilterPath -in '',$null) { + foreach ($p in $LatestPaths) { + if ($FilterPath -ne '') { $FilterPath = (@($FilterPath,"$($p)quad/","$($p)staging/","$($p)scrim/","$($p)tourney/") -join ',') } + else { $FilterPath = "$($p)quad/,$($p)staging/,$($p)scrim/,$($p)tourney/" } + } + } else { + $temp = $FilterPath -split ',' + $FilterPath = '' + foreach ($fp in $temp) { + if ($fp -notmatch '/$') { $fp = "$fp/"} + foreach ($p in $LatestPaths) { + if ($FilterPath -ne '') { $FilterPath = (@($FilterPath,"$($p)$fp") -join ',') } + else { $FilterPath = "$($p)$fp" } + } + } + } +} + +if ($StartDateTime) { $StartDT = [DateTime]::Parse($StartDateTime) } +else { $StartDT = [DateTime]::Now } + +if ($StartOffsetDays -gt 0 -or $StartOffsetHours -gt 0) { + $StartDT = $StartDT.AddDays($StartOffsetDays * -1).AddHours($StartOffsetHours * -1) + $EndDT = [DateTime]::Now +} elseif (!$EndDateTime) { + $EndDT = $StartDT.AddDays(1) +} else { + $EndDT = [DateTime]::Parse($EndDateTime) +} + +if ($EndDT -lt $StartDT) { + $temp = $StartDT + $StartDT = $EndDT + $EndDT = $temp + Remove-Variable temp +} + +if ($OutFile -and (Test-Path -LiteralPath $OutFile)) { + $outJson = (Get-Content -LiteralPath $OutFile -Raw) | ConvertFrom-Json +} else { + $outJson = $null +} + +$filesBatched = @() +foreach ($path in ($FilterPath -split ',')) { + #if ($FilterPath -match '([A-Za-z0-9-_]+[\\/][A-Za-z0-9-_]+[\\/]).+[.]json$') { + if ($FilterPath -match '[.]json$') { + $files = (Get-Item -LiteralPath $FilterPath) + $path = (GetPathFromFileName $path) + } else { + if (!(Test-Path -LiteralPath "$PSScriptRoot/$path") -or !(Test-Path "$PSScriptRoot/$path*_stats.json")) { continue } + $files = (Get-ChildItem "$PSScriptRoot/$path*_stats.json") + } + + foreach ($f in $files) { + $inJson = (Get-Content -LiteralPath $f -Raw | ConvertFrom-Json) + $names = @($inJson.SummaryAttack.Name + $inJson.SummaryDefence.Name) + $fileDT = [datetime]::ParseExact((($f.Name -replace '^(\d\d\d\d-\d\d-\d\d[_-]\d\d-\d\d-\d\d).*$','$1') -replace '_','-'),'yyyy-MM-dd-HH-mm-ss',$null) + + if (<#($FilterPath -notmatch '[.]json$') -and#> ($fileDT -lt $StartDT -or $fileDT -gt $EndDT)) { continue } + + #if ($path + ($f.Name -replace '_blue_vs_red_stats.json','') -in $outJson.Matches.Match ` + # -or ($f.Name -replace '_blue_vs_red_stats.json','') -in $outJson.Matches.Match) { + if ($outJson.Matches.Match -match "$($f.Name -replace '_blue_vs_red(_vs_yell_vs_gren)?_stats.json','' -replace $regExReplaceFix)$") { + Write-Host "SKIPPED - Match already in the JSON: $path$($f.Name -replace '_blue_vs_red(_vs_yell_vs_gren)?_stats.json','')" + continue + } elseif (!$AllowUnranked -and $names -notmatch '#\d{1,5}$') { + Write-Host "SKIPPED - Unranked Match not allowed: $path$($f.Name -replace '_blue_vs_red(_vs_yell_vs_gren)?_stats.json','')" + continue + } elseif ($inJson.Matches.Winner -in '',$null) { + Write-Host "SKIPPED - No result found in match: $path$($f.Name -replace '_blue_vs_red(_vs_yell_vs_gren)?_stats.json','')" + continue + } elseif ($PlayerCount -and (($names | Sort-Object -Unique).Count) -notmatch $PlayerCount) { + Write-Host "SKIPPED - PlayerCount not equal to $($PlayerCount): $path$($f.Name -replace '_blue_vs_red(_vs_yell_vs_gren)?_stats.json','')" + continue + } elseif ($ExcludeFile -and (Test-Path $ExcludeFile) -and ($f -in (Get-Content $ExcludeFile))) { + Write-Host "SKIPPED - File exclusion list: $f" + continue + } + + $filesBatched += @($f) + } +} + +$filesBatched = $filesBatched | Sort-Object Name +$i = 0 +foreach ($f in $filesBatched) { + $newJson = (Get-Content -LiteralPath $f -Raw) | ConvertFrom-Json + #if ($newJson.SummaryAttack.Count -lt 4) { continue } + if ('' -in $newJson.Matches.Match) { continue } + elseif ($null -in $newJson.Matches.Match) { continue } + + Write-Host "Adding file to JSON:- $f" + if (!$outJson) { $outJson = (Get-Content -LiteralPath $f -Raw) | ConvertFrom-Json } + else { $outJson = processFoStatsJSON -CurrentJson $outJson -NewJson ((Get-Content -LiteralPath $f -Raw) | ConvertFrom-Json) } + $i++ +} + +if (!$OutFile) { + if ($Region) { + $OutFile = "statsjoin_$($region)_$('{0:yyyy-MM-dd_HHmmss}' -f (Get-Date))_$($i)matches.json" + } else { + $OutFile = "statsjoin_$('{0:yyyy-MM-dd_HHmmss}' -f (Get-Date))_$($i)matches.json" + } +} + +if ($i -lt 1) { Write-Host "No batch files found to be added to $OutFile" } + +if ($outJson) { + if ($OutFile -match '[\\/]' -and !(Test-Path -LiteralPath (Split-Path -LiteralPath $OutFile))) { + New-Item -Path (Split-Path -LiteralPath $OutFile) -ItemType Directory + } + $outJson | ConvertTo-Json | Out-File $OutFile -Encoding utf8 + Write-Host "JSON has been generated - $i files added" + Generate-DailyStatsHTML -JSON $outJson | Out-File -LiteralPath ($OutFile -replace '\.json$','.html') -Encoding utf8 + Write-Host "Batch HTML - Generated :- $($OutFile -replace '\.json$','.html')" +} + +Write-Host '============================================' + + + +# SIG # Begin signature block +# MIIboAYJKoZIhvcNAQcCoIIbkTCCG40CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB +# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR +# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUpZvtABt33/fegen1CIDvX3R9 +# DDygghYVMIIDCDCCAfCgAwIBAgIQVxZN0cTEa7NFKtjIhSbFETANBgkqhkiG9w0B +# AQsFADAcMRowGAYDVQQDDBFIYXplIEF1dGhlbnRpY29kZTAeFw0yMzAyMTAwNTM3 +# MzRaFw0yNDAyMTAwNTU3MzRaMBwxGjAYBgNVBAMMEUhhemUgQXV0aGVudGljb2Rl +# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1M46e4LcpTytNDpPe4AN +# aUJrafdLCl23kH5G7qTsBRRwI6qpRhZc5TBI19oEwulC4t8h7nI6D78kYx8FdI2h +# tb0wWmhcPBAAT7iywe0G0Q3NgZxZKOyI0yto69Z7TMPnbGKhCegPuvAT0LejgTrK +# +OAH0a/uGBVCGgu1EsIOtVitWsuxTKNR5bX3b2Zoc1xaEVOMFGy74IvXzIx+VyaN +# pSH6JYo3iSLWmQNRMBvMPsRfcvkh9R1DXemAJX3LHEm0Bei3xco+20zhRQtCO1Md +# rAEAL3aq3oFDQ2KV1RCNqo5kvnwBBDumAveg7JnjuUd3DvqCF0+Hs+q2KjRy02vt +# MQIDAQABo0YwRDAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMw +# HQYDVR0OBBYEFFwWkpE7S3mEJiqQ8mTrbI0pUNm+MA0GCSqGSIb3DQEBCwUAA4IB +# AQC9XvfYZgwF/9CysiCloHccqkH8T+AJJCX1Uq1lZ9DTO7olZfTmrI/JQWrSDdrK +# hsEi9wp6uTjfapE18EQW9CBIAMNLFjhFv/uLGEp9vZpUWpuG+2hqVu0thtZbw/Gm +# gMh8yhm/lTXUj1tcltPDkgWnd2u44O2fdF2kE6hevBEXM71a45OiMic3SgpLi6m3 +# nMrsdQ0wu3qDm5I2Tm/Htq7Telmq0V3Lu1CKK6lYxxR1+Epsxumyiu0q9IKeKLMR +# RlbKKqHPcKoOadWNfZ1kR+5sO4q3+Tnc1evHS4HbJHQVZtR2k/tdsOvNi8qk9Sh9 +# +GFTkJDGhZR9TsyW2Se2KTUMMIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAY +# WjANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl +# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdp +# Q2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5 +# MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkw +# FwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVz +# dGVkIFJvb3QgRzQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBz +# aN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbr +# VsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTR +# EEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJ +# z82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyO +# j4DatpGYQJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6R +# AXwhTNS8rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k +# 98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJ +# tppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUa +# dmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZB +# dd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVf +# nSD8oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0T +# AQH/BAUwAwEB/zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0j +# BBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsG +# AQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29t +# MEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNl +# cnRBc3N1cmVkSURSb290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9j +# cmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYD +# VR0gBAowCDAGBgRVHSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3Qb +# PbYW1/e/Vwe9mqyhhyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5 +# +KH38nLeJLxSA8hO0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+n +# BgMTdydE1Od/6Fmo8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc +# /RzY9HdaXFSMb++hUD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVr +# zyerbHbObyMt9H5xaiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o +# 4rmUMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipeWzANBgkqhkiG9w0BAQsF +# ADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +# ExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJv +# b3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1OTU5WjBjMQswCQYDVQQG +# EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0 +# IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMIICIjAN +# BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1BkmzwT1ySVFVxyUDxPKRN6mX +# UaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkLf50fng8zH1ATCyZzlm34 +# V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C3GeO6lE98NZW1OcoLevT +# sbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5n12sy+iEZLRS8nZH92GD +# Gd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUdzTYNXNXmG6jBZHRAp8By +# xbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWHpo9OdhVVJnCYJn+gGkcg +# Q+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/oN7jPqJz+ucfWmyU8lKV +# EStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPVA+C/8KI8ykLcGEh/FDTP +# 0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg0ixXNXkrqPNFYLwjjVj3 +# 3GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mMDDtbiiKowSYI+RQQEgN9 +# XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6EVO7O6V3IXjASvUaetdN2 +# udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYD +# VR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1UdIwQYMBaAFOzX44LScV1k +# TN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcD +# CDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj +# ZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t +# L0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0 +# cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmww +# IAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUA +# A4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBTzr8Y+8dQXeJLKftwig2q +# KWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/EUExiHQwIgqgWvalWzxVz +# jQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fmniye4Iqs5f2MvGQmh2yS +# vZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szwcqMj+sAngkSumScbqyQe +# JsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8THwcFqcdnGE4AJxLafzYeH +# JLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/JWOZJyw9P2un8WbDQc1P +# tkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9Pzt4rUyt+8SVe+0KXzM5 +# h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm228Vex4Ziza4k9Tm8heZ +# Wcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVBtzrVFZgxtGIJDwq9gdkT +# /r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnwZXZCpimHCUcr5n8apIUP +# /JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv27dZ8/DCCBsIwggSqoAMC +# AQICEAVEr/OUnQg5pr/bP1/lYRYwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMC +# VVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBU +# cnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yMzA3 +# MTQwMDAwMDBaFw0zNDEwMTMyMzU5NTlaMEgxCzAJBgNVBAYTAlVTMRcwFQYDVQQK +# Ew5EaWdpQ2VydCwgSW5jLjEgMB4GA1UEAxMXRGlnaUNlcnQgVGltZXN0YW1wIDIw +# MjMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjU0WHHYOOW6w+VLMj +# 4M+f1+XS512hDgncL0ijl3o7Kpxn3GIVWMGpkxGnzaqyat0QKYoeYmNp01icNXG/ +# OpfrlFCPHCDqx5o7L5Zm42nnaf5bw9YrIBzBl5S0pVCB8s/LB6YwaMqDQtr8fwkk +# lKSCGtpqutg7yl3eGRiF+0XqDWFsnf5xXsQGmjzwxS55DxtmUuPI1j5f2kPThPXQ +# x/ZILV5FdZZ1/t0QoRuDwbjmUpW1R9d4KTlr4HhZl+NEK0rVlc7vCBfqgmRN/yPj +# yobutKQhZHDr1eWg2mOzLukF7qr2JPUdvJscsrdf3/Dudn0xmWVHVZ1KJC+sK5e+ +# n+T9e3M+Mu5SNPvUu+vUoCw0m+PebmQZBzcBkQ8ctVHNqkxmg4hoYru8QRt4GW3k +# 2Q/gWEH72LEs4VGvtK0VBhTqYggT02kefGRNnQ/fztFejKqrUBXJs8q818Q7aESj +# pTtC/XN97t0K/3k0EH6mXApYTAA+hWl1x4Nk1nXNjxJ2VqUk+tfEayG66B80mC86 +# 6msBsPf7Kobse1I4qZgJoXGybHGvPrhvltXhEBP+YUcKjP7wtsfVx95sJPC/QoLK +# oHE9nJKTBLRpcCcNT7e1NtHJXwikcKPsCvERLmTgyyIryvEoEyFJUX4GZtM7vvrr +# kTjYUQfKlLfiUKHzOtOKg8tAewIDAQABo4IBizCCAYcwDgYDVR0PAQH/BAQDAgeA +# MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkw +# FzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaa +# L3WMaiCPnshvMB0GA1UdDgQWBBSltu8T5+/N0GSh1VapZTGj3tXjSTBaBgNVHR8E +# UzBRME+gTaBLhklodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz +# dGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcB +# AQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFgG +# CCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRU +# cnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3 +# DQEBCwUAA4ICAQCBGtbeoKm1mBe8cI1PijxonNgl/8ss5M3qXSKS7IwiAqm4z4Co +# 2efjxe0mgopxLxjdTrbebNfhYJwr7e09SI64a7p8Xb3CYTdoSXej65CqEtcnhfOO +# HpLawkA4n13IoC4leCWdKgV6hCmYtld5j9smViuw86e9NwzYmHZPVrlSwradOKmB +# 521BXIxp0bkrxMZ7z5z6eOKTGnaiaXXTUOREEr4gDZ6pRND45Ul3CFohxbTPmJUa +# VLq5vMFpGbrPFvKDNzRusEEm3d5al08zjdSNd311RaGlWCZqA0Xe2VC1UIyvVr1M +# xeFGxSjTredDAHDezJieGYkD6tSRN+9NUvPJYCHEVkft2hFLjDLDiOZY4rbbPvlf +# sELWj+MXkdGqwFXjhr+sJyxB0JozSqg21Llyln6XeThIX8rC3D0y33XWNmdaifj2 +# p8flTzU8AL2+nCpseQHc2kTmOt44OwdeOVj0fHMxVaCAEcsUDH6uvP6k63llqmjW +# Iso765qCNVcoFstp8jKastLYOrixRoZruhf9xHdsFWyuq69zOuhJRrfVf8y2OMDY +# 7Bz1tqG4QyzfTkx9HmhwwHcK1ALgXGC7KP845VJa1qwXIiNO9OzTF/tQa/8Hdx9x +# l0RBybhG02wyfFgvZ0dl5Rtztpn5aywGRu9BHvDwX+Db2a2QgESvgBBBijGCBPUw +# ggTxAgEBMDAwHDEaMBgGA1UEAwwRSGF6ZSBBdXRoZW50aWNvZGUCEFcWTdHExGuz +# RSrYyIUmxREwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAw +# GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisG +# AQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFHnPPEzxdl5V+kNkLQUfRyt4K8bmMA0G +# CSqGSIb3DQEBAQUABIIBACv0W9Etwgiq8Hm0BSFfv+JwADrd2UlVOh9fUKb7Ejeo +# O7cliZNojdndN0rSRkJxet+To2ZbmgzefPUSzxsChQvEcGDoDgrBvGAi1zgGGmPb +# shpyhsRRIrxQ7zSs/FeYZ5ao0P9CLPJ2DIWpsJV17Bv+oUoTAxkgNfAfaw6wEe/4 +# uubetLFaws4KDdwzkzlkh5veT12sDDvnP0W9fBYCXsVMqhNDN0Tn78p8Nl4lH8OY +# cCmvq1u4ZHl30Y8c0MBPdQLitMjb9CU9/n0z9d+T4/i6py40OmqjzEzgG16yqzFk +# +3AFT5MlXpese7u8gXQqMF25Hn64zqVYD++qeZog2iGhggMgMIIDHAYJKoZIhvcN +# AQkGMYIDDTCCAwkCAQEwdzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl +# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT +# SEEyNTYgVGltZVN0YW1waW5nIENBAhAFRK/zlJ0IOaa/2z9f5WEWMA0GCWCGSAFl +# AwQCAQUAoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUx +# DxcNMjMwODIyMDUyOTAwWjAvBgkqhkiG9w0BCQQxIgQgIvSAORWs8v2L0/1SzAIg +# OXyuk4gDFBB6pN2J+wilLKYwDQYJKoZIhvcNAQEBBQAEggIAUhu9RGgkZcIXgc+C +# njenTCL0kZPRD0k5wsiaXyNdPkIwvSqjqG+oVPTtasBNEmf/nnEIuQ9bsJeOTxlU +# vBDP1a7SDUBwhe0VSG17u37Gv9bRIJFylmZ/97iysfv/eWb2GBNzg7MPWlAfujMV +# cetnxRvt7QCH9HgNEpxGBufjPj+EvFsdu8BX8GrIS3ckc0O/5nKskcjcC3Xz9ucY +# 04kf9SLpMseYYkYrt8PnlmgkhNOzahxUSxoPhfhIViCBh+o5nXqQrywNCVux6zeV +# 6uNCln4SUsTPCf2fdse88dDI3E6UiDJy4ygvzd6+HAK7ZDgWwvFJc0O7NFenqNLa +# bYuj2q3ZHP3XkLsQAHyWBkefsGOOnYLKi6Y/wPdEwK72w8X/IwTvKrdyl6oixrsQ +# qe1cgenh5eHBaETbfQD8ShESh1+ZQtIy9XU0GRhhidmjeEQeySFcgRsC5B2DJ3IH +# tDoyXxxA6JzL3Q8P3WxTlUnsT7uJuyeiiJ1IA6ZkCdFN4CHNVqAZj27x3UgQOWzV +# ELq3YPWR5PfItjioyuDiuNL7QzUxIbODBfRFuSgw89fXJVn7opkwSYO4FnCe56U+ +# sAdQeacyXJbGZZFCU9Tpl1t2OrfmTrmbOar2lj2iUP4DIvTyrCdgRMlzQ5ieRWVR +# b8HP8/o9CMN40c2wfgUhTGrvx0Y= +# SIG # End signature block diff --git a/FO_stats_v1f.ps1 b/FO_stats_v1f.ps1 deleted file mode 100644 index 7e7e811..0000000 --- a/FO_stats_v1f.ps1 +++ /dev/null @@ -1,1768 +0,0 @@ -### -# 11/5/21 23:00 -# PS COMMAND LINE:- & .\script.ps1 'x:\path\filename.json' [] -# WIN COMMAND LINE:- powershell -Command "& .\script.ps1" "x:\path\filename.json" [] -# PS *.JSON:- foreach ($f in (gci 'H:\stats\*.json')) { & .\FO_stats_v1.ps1 ($f.ToString() -replace '\[','`[' -replace '\]','`]') } -### - -$regExReplaceFix = '[[+*?()\\.]','\$&' - -$ccGrey = '#F1F1F1' -$ccAmber = '#FFD900' -$ccOrange = '#FFB16C' -$ccGreen = '#96FF8F' -$ccBlue = '#87ECFF' -$ccRed = '#FF8080' -$ccPink = '#FA4CFF' - - # 0 1 2 3 4 5 6 7 8 9 10 -$ClassToStr = @('World','Sco','Snp','Sold','Demo','Med','HwG','Pyro','Spy','Eng', 'SG') -$ClassAllowed = @(1,3,4,5,6,7,8,9) -$ClassAllowedwithSG = @(1,3,4,5,6,7,8,9,10) -$TeamToColor = @('Civ','Blue','Red','Yellow','Green') - -#array of team keys, playername -function getPlayerClasses { - $class = '' - $arrClass = $args[0] -match "^$($args[1] -replace $regExReplaceFix)_[1-9]`$" - - foreach ($k in $arrClass) { - $c = $ClassToStr[($k -split '_')[1]] - - if ($class -eq '') { - $class = $c - } elseif ($c -notin ($class -split ',')) { - $class += ", $($c)" - } - } - $class -} - -function nullValueColorCode { - switch ($args[0]) { - '' { $ccGrey } - default { $args[1] } - } -} - -function teamColorCode { - switch ($args[0]) { - '1' { $ccBlue } - '2' { $ccRed } - default { $ccPink } - } -} - -# Friendly fire is bad -function actionColorCode ($arrTeam, $p1, $p2) { - #check if action is no Friendly fire - if ($p1 -eq $p2) { - $ccAmber #yellow - } elseif ($arrTeam.$p1 -eq $arrTeam.$p2) { - #Friendly fire - $ccOrange #orange - } else { - $ccGreen #green - } -} - -function timeRnd-RoundDown { - # p1 = $time , $p2 = roundendtime - if ($args[0] / 60 -lt 0.4) { - if ($args[0] -lt $args[1]) { '0' } - else { $args[1] } - }else { $time } -} - -# Convert weapon names into friendly short names -function weapSN { - - switch ($args[0]) { - #common - 'info_tfgoal' { 'laser' } - 'supershotgun' { 'ssg' } - 'shotgun' { 'sg' } - 'normalgrenade' { 'hgren' } - 'grentimer' { 'hgren' } - 'axe' { 'axe' } - 'spike' { 'ng' } - - #scout - 'flashgrenade' { 'flash' } - - #sold - 'proj_rocket' { 'rl' } - 'shockgrenade' { 'shock' } - - #demo - 'detpack' { 'detp' } - 'pipebomb' { 'pipe' } - 'grenade' { 'gl' } - 'mirvsinglegrenade' { 'mirv' } - 'mirvgrenade' { 'mirv' } - - #medic - 'medikit' { 'bio' } - 'superspike' { 'sng' } - - #hwg - 'proj_bullet' { 'cann' } - - #pyro - 'pyro_rocket' { 'incen' } - 'fire' { 'fire' } - 'flamerflame' { 'flame' } - - #spy - knife - 'proj_tranq' { 'tranq' } - - #eng - 'spanner' { 'spann' } - 'empgrenade' { 'emp' } - 'sentrygun' { 'sent' } - 'railslug' { 'rail' } - 'railgun' { 'rail' } - 'building_dispenser' { 'disp' } - 'building_sentrygun' { 'sent' } - 'build_timer' { 'sent' } - - #remove underscore to avoid token key issues. - default { $args[0] -replace '_','-' } - } -} - -# Process input file, make sure it is valid. -$inputFileStr = [String]$args[0] -replace '(? - -<# old magoo based on rank... changed to % above average -$tkRank = ($awardDefTKill.GetEnumerator() | Sort -Property Value) -$tdRank = ($awardDefDmgTeam.GetEnumerator() | Sort -Property Value) - -$awardDefMagoo = @{} -$score = 0 # score is count of pst -$count = 1 # keep track of playeer pos -foreach ($p in $tkRank.Key) { - if ($tkMax -ne $awardDefTkill.$p -or $tkMax -eq '') { - $tkMax = $awardDefTKill.$p - $score = $count - } - $awardDefMagoo.$p += $score - $prevScore = $awardDefMagoo.$p - $count += 1 -} - -$score = 0 # score is count of pst -$count = 1 # keep track of playeer pos -foreach ($p in $tdRank.Key) { - $adjDMG = [Math]::Round($awardDefDmgTeam.$p / 100) * 100 - if ($tdMax -ne $adjDMG -or $tdMax -eq '') { - $tdMax = $adjDMG - $score = $count - } - $awardDefMagoo.$p += $score - $prevScore = $awardDefMagoo.$p - $count += 1 -} -Remove-Variable adjDMG,prevScore,tdMax,tkMax,score -#> - - -# MR Magoo % higher than TK and TmDmg, then divide by two for an average. -$tkAvg = ($awardDefTKill.Values | Measure-Object -Average).Average -$tdAvg = ($awardDefDmgTeam.Values | Measure-Object -Average).Average -$awardDefMagoo = @{} -foreach ($p in $awardDefTKill.Keys) { $awardDefMagoo.$p += $awardDefTKill.$p / $tkAvg } -foreach ($p in $awardDefDmgTeam.Keys) { $awardDefMagoo.$p = [Math]::Round(($awardDefMagoo.$p + ($awardDefDmgTeam.$p / $tdAvg))/2,2) } -Remove-Variable tkAvg,tdAvg - - -# Repeatable function for Killed Class Lookup -function awardFromKilledClass { - # p1 = att/def p2 = regex - $htOut = @{} - switch ($args[0]) { - 'Att' { $plRnd1 = $playerListAttRnd1; $plRnd2 = $playerListAttRnd2 } - 'Def' { $plRnd1 = $playerListDefRnd1; $plRnd2 = $playerListDefRnd2 } - Default { return } - } - - foreach ($item in $arrKilledClassRnd1.Keys -match $args[1]) { - $name = ($item -split '_')[0] - if ($name -in $plRnd1) { $htOut.$name += $arrKilledClassRnd1.$item } - } - foreach ($item in $arrKilledClassRnd2.Keys -match $args[1]) { - $name = $($item -split '_')[0] - - if ($name -in $plRnd2) { $htOut.$name += (awardScaler $arrKilledClassRnd2.$item) } - } - - if ($htOut.Count -lt 1) { $htOut.'n/a' = 'n/a' } - return $htOut -} - -$awardAttKilledDemo = (awardFromKilledClass 'Att' '.*_4$') -$awardDefKilledSold = (awardFromKilledClass 'Def' '.*_3$') -$awardDefKilledLight = (awardFromKilledClass 'Def' '.*_(1|5|8|9)$') -$awardAttKilledHeavy = (awardFromKilledClass 'Att' '.*_(3|4|6|7)$') -$awardDefKilledHeavy = (awardFromKilledClass 'Def' '.*_(3|4|6|7)$') -$awardAttLightKills = (awardFromKilledClass 'Att' '.*_(1|5|8|9)_.*$') -$awardAttKilledSG = (awardFromKilledClass 'Att' '.*_10$') - - -##### -#Get MAX and MIN for each of the new tables starting with awardAtt/awardDef -# CREATE ALL $awardDef and $awardAtt tables BEFORE THIS POINT!! -###### -#attack -#### -foreach ($v in (Get-Variable 'award*' -Exclude '*_*')) { - Set-Variable -Name "$($v.Name)_Max" -Value (($v.Value).Values | Measure-Object -Maximum).Maximum - Set-Variable -Name "$($v.Name)_Min" -Value (($v.Value).Values | Measure-Object -Minimum).Minimum -} - -function addTokenToString { - if ($args[0] -in $null,'') { $args[1] } - elseif ($args[1] -notin ($args[0] -split ', ')) { "$($args[0]), $($args[1])" } - else { $args[0] } -} - -# Get the Names of each Max/Min award table -foreach ($p in $PlayerList) { - foreach ($vlist in @((Get-Variable 'award*_Max' -Exclude '*Versus*'),(Get-Variable 'award*_Min' -Exclude '*Versus*'))) { - foreach ($v in $vlist) { - $arrayName = ($v.Name -Split '_')[0] - $name = "$($v.Name)Name" - $value = $v.Value[1] - if (Test-Path "variable:$($name)") { $leader = Get-Variable $name -ValueOnly } - else { $leader = '' } - - if ((Get-Variable $arrayName).Value.$p -eq $v.Value) { Set-Variable -Name $name -Value (addTokenToString $leader $p) } - } - } -} - -# Most Frag on a player -$attMax = '' -$awardAttPlayerFrag_MaxName = '' -$awardAttPlayerFrag_Victim = '' -$awardAttPlayerFrag_Value = '' -$defMax = '' -$awardDefPlayerFrag_MaxName = '' -$awardDefPlayerFrag_Victim = '' -$awardDefPlayerFrag_Value = '' - -$count = 1 -### Frag versus statistics -foreach ($array in @($arrFragVersusRnd1,$arrFragVersusRnd2)) { - foreach ($item in $array.keys) { - #player/target - $pt = $item -split '_' - switch ($count) { - 1 { $pl = $playerListAttRnd1; $value = $array.$item } - default { $pl = $playerListAttRnd2; $value = awardScaler $array.$item } - } - - if ($pt[0] -ne $pt[1] -and $pt[0] -in $pl) { - if ($max -eq '' -or $value -ge $attMax) { - if ($value -eq $attMax) { - $awardAttPlayerFrag_MaxName = (addTokenToString $awardAttPlayerFrag_MaxName $pt[0]) - $awardAttPlayerFrag_Victim = (addTokenToString $awardAttPlayerFrag_Victim $pt[1]) - } else { - $awardAttPlayerFrag_MaxName = $pt[0] - $awardAttPlayerFrag_Victim = $pt[1] - } - $awardAttPlayerFrag_Value = $value - $attMax = $value - } - } - - switch ($count) { - 1 { $pl = $playerListDefRnd1 } - default { $pl = $playerListDefRnd2 } - } - - if ($pt[0] -ne $pt[1] -and $pt[0] -in $pl) { - if ($max -eq '' -or $value -ge $defMax) { - if ($value -eq $defMax) { - $awardDefPlayerFrag_MaxName = (addTokenToString $awardDefPlayerFrag_MaxName $pt[0]) - $awardDefPlayerFrag_Victim = (addTokenToString $awardDefPlayerFrag_Victim $pt[1]) - } else { - $awardDefPlayerFrag_MaxName = $pt[0] - $awardDefPlayerFrag_Victim = $pt[1] - } - $awardDefPlayerFrag_Value = $value - $defMax = $value - } - } - } - $count += 1 -} - -# Most Dmg on a player -$attMax = '' -$awardAttPlayerDmg_MaxName = '' -$awardAttPlayerDmg_Victim = '' -$awardAttPlayerDmg_Value = '' -$defMax = '' -$awardDefPlayerDmg_MaxName = '' -$awardDefPlayerDmg_Victim = '' -$awardDefPlayerDmg_Value = '' - -$count = 1 -### Damage versus statistics -foreach ($array in @($arrDmgVersusRnd1,$arrDmgVersusRnd2)) { - foreach ($item in $array.keys) { - #player/target - $pt = $item -split '_' - switch ($count) { - 1 { $pl = $playerListAttRnd1; $value = $array.$item } - default { $pl = $playerListAttRnd2; $value = awardScaler $array.$item } - } - - if ($pt[0] -ne $pt[1] -and $pt[0] -in $pl) { - if ($max -eq '' -or $value -ge $attMax) { - if ($value -eq $attMax) { - $awardAttPlayerDmg_MaxName = (addTokenToString $awardAttPlayerDmg_MaxName $pt[0]) - $awardAttPlayerDmg_Victim = (addTokenToString $awardAttPlayerDmg_Victim $pt[1]) - } else { - $awardAttPlayerDmg_MaxName = $pt[0] - $awardAttPlayerDmg_Victim = $pt[1] - } - $awardAttPlayerDmg_Value = $value - $attMax = $value - } - } - - switch ($count) { - 1 { $pl = $playerListDefRnd1 } - default { $pl = $playerListDefRnd2 } - } - - if ($pt[0] -ne $pt[1] -and $pt[0] -in $pl) { - if ($max -eq '' -or $value -ge $defMax) { - if ($value -eq $defMax) { - $awardDefPlayerDmg_MaxName = (addTokenToString $awardDefPlayerDmg_MaxName $pt[0]) - $awardDefPlayerDmg_Victim = (addTokenToString $awardDefPlayerDmg_Victim $pt[1]) - } else { - $awardDefPlayerDmg_MaxName = $pt[0] - $awardDefPlayerDmg_Victim = $pt[1] - } - $awardDefPlayerDmg_Value = $value - $defMax = $value - } - - } - } - $count += 1 -} - - - -#### -# Make Award HTML String -### - -function awardScaleCaveat { - # p1 = att/def #p2 = names - if ($arrResult.winningTeam -lt 2) { return $args[1] } - - $players = $args[1] -split', ' - switch ($args[0]) { - 'Att' { $pl = $playerListAttRnd2 } - default { $pl = $playerListDefRnd2 } - } - - $outNames = '' - $count = 1 - foreach ($p in $players) { - if ($p -in $pl) { $name = "$($p)*" } - else { $name = $p } - - if ($outNames -eq '') { $outNames = $name } - else { $outNames += ", $($name)" } - } - return $outNames -} - - -$awardsHtml = "
-
','
' -replace '
- - - - - - - - - - - - - - - -" - -if ($arrResult.winningTeam -eq 2) { - $awardsHtml += "" -} - -$awardsHtml += "

The Attackers

Award Winner Description
Commando $(awardScaleCaveat 'Att' $awardAttKills_MaxName) Most kills ($($awardAttKills_Max))
Rambo $(awardScaleCaveat 'Att' $awardAttDmg_MaxName) Most damage ($($awardAttDmg_Max))
Golden Hands $($awardAttFlagCap_MaxName) Most caps ($($awardAttFlagCap_Max))
Running Man $($awardAttFlagTime_MaxName) Most time with flag ($($awardAttFlagTime_Max)s)
Brawler $(awardScaleCaveat 'Att' $awardAttKilledHeavy_MaxName)Most kills on heavy classes ($($awardAttKilledHeavy_Max))
David $(awardScaleCaveat 'Att' $awardAttLightKills_MaxName) Most kills as a light class ($($awardAttLightKills_Max))
Spec Ops $(awardScaleCaveat 'Att' $awardAttKilledDemo_MaxName) Most kills on demo ($($awardAttKilledDemo_Max))
Sapper $(awardScaleCaveat 'Att' $awardAttKilledSG_MaxName) Most kills on a SG ($($awardAttKilledSG_Max))
Lemming $(awardScaleCaveat 'Att' $awardAttDeath_MaxName) Most Deaths ($($awardAttDeath_Max))
Battering Ram $(awardScaleCaveat 'Att' $awardAttKD_MinName) Lowest Kill-Death rank ($($awardAttKD_Min))
Buck shot $($awardAttDmgPerKill_MaxName) Most Damage per kill ($($awardAttDmgPerKill_Max))
Predator $(awardScaleCaveat 'Att' $awardAttPlayerFrag_MaxName) Most kills on a defender ($($awardAttPlayerFrag_Value) on $($awardAttPlayerFrag_Victim))
Hulk Smash $(awardScaleCaveat 'Att' $awardAttPlayerDmg_MaxName) Most damage on a defender ($($awardAttPlayerDmg_Value) on $($awardAttPlayerDmg_Victim))
*Team2 scaled: Only $('{0:p0}' -f [math]::Round((1 - $arrResult.winRating),2)) of Rnd2 played
- -
- - - - - - - - - - - - - - - -" - -if ($arrResult.winningTeam -eq 2) { - $awardsHtml += "" -} -$awardsHtml += "

The Defenders

Award Winner Description
Slaughterhouse $(awardScaleCaveat 'Def' $awardDefKills_MaxName) Most kills ($($awardDefKills_Max))
Terminator $(awardScaleCaveat 'Def' $awardDefKD_MaxName) Kills-death rank ($($awardDefKD_Max))
Juggernaut $(awardScaleCaveat 'Def' $awardDefDmg_MaxName) Most damage ($($awardDefDmg_Max))
Dark Knight $(awardScaleCaveat 'Def' $awardDefKilledSold_MaxName) Most kills on Soldier ($($awardDefKilledSold_Max))
Tank $(awardScaleCaveat 'Def' $awardDefKilledHeavy_MaxName)Most kills on a heavy class ($($awardDefKilledHeavy_Max))
Goliath $(awardScaleCaveat 'Def' $awardDefKilledLight_MaxName)Most kills on a light class ($($awardDefKilledLight_Max))
Sly Fox $(awardScaleCaveat 'Def' $awardDefDeath_MinName) Lowest Deaths ($($awardDefDeath_Min))
Team Player $($awardDefDmgPerKill_MaxName) Most damage per kill ($($awardDefDmgPerKill_Max))
Nemesis $(awardScaleCaveat 'Def' $awardDefPlayerFrag_MaxName) Most Kills on an attacker ($($awardDefPlayerFrag_Value) on $($awardDefPlayerFrag_Victim))
No quarter $($awardDefDmgPerKill_MinName) Lowest damage per kill ($($awardDefDmgPerKill_Min))
Attention whore $(awardScaleCaveat 'Def' $awardDefDmgAll_MaxName) Most damage given + taken ($($awardDefDmgAll_Max))
Shy Guy $(awardScaleCaveat 'Def' $awardDefDmgTaken_MinName) Lowest damage taken ($($awardDefDmgTaken_Min))
Mr Magoo $($awardDefMagoo_MaxName) Team Kill/Damage above avg ($('{0:p0}' -f $awardDefMagoo_Max))
*Team1 scaled: Only $('{0:p0}' -f [math]::Round((1 - $arrResult.winRating),2)) of Rnd2 played
" - -### -# Generate the HTML Ouput -### -$htmlOut = " - - - - -

$($inputFile.Name)

" - - -$htmlOut += " - -
ResultScoresWin Rating
" - -switch ($arrResult.winningTeam) { - '0' { $htmlOut += "DRAW! " } - default { $htmlOut += "TEAM $($arrResult.winningTeam) WINS! " } -} - -$htmlOut += "Team1: $($arrResult.team1Score) vs Team2: $($arrResult.team2Score)$('{0:p0}' -f $arrResult.winRating) ($($arrResult.winRatingDesc))
" - -#### awards -$htmlOut += "

Awards

" -$htmlOut += $awardsHtml - - - -#Frag Total Table -$htmlOut += "

TOTAL - Frags and Damage

" - -$count = 1 -$tableHeader = "" - - $table += "" - - $count2 = 0 - foreach ($o in $playerList) { - $key = "$($p)_$($o)" - $kills = $arrFragVersus.$key - if ($kills -eq '' -or $kills -lt 1) { $kills = 0 } - - $table += "" - - $subtotal[$count2] = $kills + $subtotal[$count2] - $count2 += 1 - } - - $table += "" - $table += "`n" - - $count += 1 -} - -$tableHeader += "" -$tableHeader += "" - -$table += '' -foreach ($st in $subtotal) { $table += "" } -$table += '' - -$htmlOut += $tableHeader -$htmlOut += $table -$htmlOut += "
#PlayerTeamKillsDthTK" -$table = '' -$subtotal = @($playerList | foreach { 0 } ) - -foreach ($p in $playerList) { - $tableHeader += "$($count)
$($count)$($p)$($arrTeam.$p)$($arrFragTotal.$p)$($arrDeath.$p)$($arrTKill.$p)$($kills)$(getPlayerClasses $arrTimeClass.Keys $p)
Classes
Total:$($st)
" - -#Damage Table - side by side with frags -#"

Damage

" -$tableHeader = "" - -$count = 1 -$table = '' -$subtotal = @($playerList | foreach { 0 } ) - -foreach ($p in $playerList) { - $tableHeader += "" - $table += "" - - $count2 = 0 - foreach ($o in $playerList) { - $key = "$($p)_$($o)" - $dmg = $arrDmgVersus.$key - if ($dmg -eq '' -or $dmg -lt 1) { $dmg = 0 } - - $table += "" - - #don't count self dmg - if ($p -ne $o) { $subtotal[$count2] = $dmg + $subtotal[$count2] } - $count2 += 1 - } - - $table += "" - $table += "`n" - - $count += 1 -} - -$tableHeader += "" -$tableHeader += "" - -$table += '' -foreach ($st in $subtotal) { $table += "" } - - -$htmlOut += $tableHeader -$htmlOut += $table -$htmlOut += "
#PlayerTeamDmg$($count)
$($count)$($p)$($arrTeam.$p)$($arrDmgTotal.$p)$($dmg)$(getPlayerClasses $arrTimeClass.Keys $p)
Classes
Total: *minus self-dmg$($st)
" - -#Frag Rnd1 v Rnd2 Table -$htmlOut += "

Frags - Round 1 and Round 2

" - -$count = 1 -$tableHeader = "$($tableStyle2)" - $table += "" - - $count2 = 0 - foreach ($o in $playerList) { - $key = "$($p)_$($o)" - $kills = $arrFragVersusRnd1.$key - if ($kills -eq '' -or $kills -lt 1) { $kills = 0 } - - $table += "" - - $subtotal[$count2] = $kills + $subtotal[$count2] - $count2 +=1 - } - - $table += "" - $table += "`n" - - $count += 1 -} - -$tableHeader += "" -$tableHeader += "" - -$table += '' -foreach ($st in $subtotal) { $table += "" } - -$htmlOut += $tableHeader -$htmlOut += $table -$htmlOut += "
#PlayerTeamKillsDthTK" -$table = '' -$subtotal = @($playerList | foreach { 0 } ) - -foreach ($p in $playerList) { - $tableHeader += "$($count)
$($count)$($p)$($arrTeamRnd1.$p)$($arrFragTotalRnd1.$p)$($arrDeathRnd1.$p)$($arrTKillRnd1.$p)$($kills)$(getPlayerClasses $arrTimeClassRnd1.Keys $p)
Classes
Total:$($st)
" - -#Frag Rnd2 Table -#"

Frags - Round 2

" - -$count = 1 -$tableHeader = "$($tableStyle2)" - $table += "" - - $count2 = 0 - foreach ($o in $playerList) { - $key = "$($p)_$($o)" - $kills = $arrFragVersusRnd2.$key - if ($kills -eq '' -or $kills -lt 1) { $kills = 0 } - - $table += "" - - $subtotal[$count2] = $kills + $subtotal[$count2] - $count2 +=1 - } - - $table += "" - $table += "`n" - - $count += 1 -} - -$tableHeader += "" -$tableHeader += "" - -$table += '' -foreach ($st in $subtotal) { $table += "" } - -$htmlOut += $tableHeader -$htmlOut += $table -$htmlOut += "
#PlayerTeamKillsDthTK" -$table = '' -$subtotal = @($playerList | foreach { 0 } ) - -foreach ($p in $playerList) { - $tableHeader += "$($count)
$($count)$($p)$($arrTeamRnd2.$p)$($arrFragTotalRnd2.$p)$($arrDeathRnd2.$p)$($arrTKillRnd2.$p)$($kills)$(getPlayerClasses $arrTimeClassRnd2.Keys $p)
Classes
Total:$($st)
" - - - -#Damage by Round Table -$htmlOut += "

Damage - Round 1 and Round 2

" - -$count = 1 -$tableHeader = "" -$table = '' -$subtotal = @($playerList | foreach { 0 } ) - -foreach ($p in $playerList) { - $tableHeader += "" - $table += "" - - $count2 = 0 - foreach ($o in $playerList) { - $key = "$($p)_$($o)" - $dmg = $arrDmgVersusRnd1.$key - if ($dmg -eq '' -or $dmg -lt 1) { $dmg = 0 } - - $table += "" - - #don't count self dmg - if ($p -ne $o) { $subtotal[$count2] = $dmg + $subtotal[$count2] } - $count2 +=1 - } - - $table += "" - $table += "`n" - - $count += 1 -} - -$tableHeader += "" -$tableHeader += "" - -$table += '' -foreach ($st in $subtotal) { $table += "" } - -$htmlOut += $tableHeader -$htmlOut += $table -$htmlOut += "
#PlayerTeamDmg$($count)
$($count)$($p)$($arrTeamRnd1.$p)$($arrDmgTotalRnd1.$p)$($dmg)$(getPlayerClasses $arrTimeClassRnd1.Keys $p)
Classes
Total: *minus self-dmg$($st)
" - -$count = 1 -$tableHeader = "" -$table = '' -$subtotal = @($playerList | foreach { 0 } ) - - -foreach ($p in $playerList) { - $tableHeader += "" - $table += "" - - $count2 = 0 - foreach ($o in $playerList) { - $key = "$($p)_$($o)" - $dmg = $arrDmgVersusRnd2.$key - if ($dmg -eq '' -or $dmg -lt 1) { $dmg = 0 } - - $table += "" - - #don't count self dmg - if ($p -ne $o) { $subtotal[$count2] = $dmg + $subtotal[$count2] } - $count2 +=1 - } - - $table += "" - $table += "`n" - - $count += 1 -} - -$tableHeader += "" -$tableHeader += "" - -$table += '' -foreach ($st in $subtotal) { $table += "" } - -$htmlOut += $tableHeader -$htmlOut += $table -$htmlOut += "
#PlayerTeamDmg$($count)
$($count)$($p)$($arrTeamRnd2.$p)$($arrDmgTotalRnd2.$p)$($dmg)$(getPlayerClasses $arrTimeClassRnd2.Keys $p)
Classes
Total: *minus self-dmg$($st)
" - -### -# frag/death per mins -### - -$htmlOut += "

Per Minute - Frags/Deaths

" - -$tableHeader = "$($tableStyle2) - -" - if ($min -in $timeBlock,([math]::Floor($round1EndTime / 60))) { - $tableHeader += "" - } -} -$tableHeader += "" - -$count = 1 -$subtotalFrg = @(1..$timeBlock | foreach { 0 } ) -$subtotalDth = @(1..$timeBlock | foreach { 0 } ) - -foreach ($p in $playerList) { - $table += "" - - $count2 = 0 - foreach ($min in 1..$timeBlock) { - $key = "$($min)_$($p)" - $kills = $arrFragMin.$key - $dth = $arrDeathMin.$key - if ($kills -in '',$null -and $dth -in '',$null) { - $value = '' - $cellCC = nullValueColorCode - } else { - $cellCC = $ccGreen - if ($kills -lt $dth) { $cellCC = $ccAmber } - if ($kills -eq '' -or $kills -lt 1) { $kills = '0'; $cellCC = $ccOrange } - if ($dth -eq '' -or $dth -lt 1) { $dth = '0' } - - $value = "$($kills)/$($dth)" - } - - $table += "" - - $subtotalFrg[$count2] += $kills - $subtotalDth[$count2] += $dth - - - if ($min -in $timeBlock,([math]::Floor($round1EndTime / 60))) { - #rnd total - if ($min -le ([math]::Floor($round1EndTime / 60))) { - $table += "" - } else { - $table += "" - } - } - $count2 += 1 - } - - $table += "`n" - $count += 1 -} - -$table += '' -$count = 0 -foreach ($st in $subtotalFrg) { - $table += "" - $count += 1 - if ($count -in $timeBlock,([math]::Floor($round1EndTime / 60))) { $table += "" } -} -$table += "" - -$htmlOut += $tableHeader -$htmlOut += $table -$htmlOut += "
Rnd1Rnd2
#PlayerTeamKillsDthTK" -$table = '' - -foreach ($min in 1..$timeBlock) { - $tableHeader += "$($min)Total
$($count)$($p)$($arrTeam.$p)$($arrFragTotal.$p)$($arrDeath.$p)$($arrTKill.$p)$($value)$([int]$arrFragTotalRnd1.$p)/$([int]$arrDeathRnd1.$p)$([int]$arrFragTotalRnd2.$p)/$([int]$arrDeathRnd2.$p)
Total:$([int]$subtotalFrg[$count])/$([int]$subtotaldth[$count])
" - -### -# Damage per mins -### -$htmlOut += "

Per Minute - Damage (excluding friendly-fire)

" -#$tableHeader = "$($tableStyle2)" - - $count2 = 0 - foreach ($min in 1..$timeBlock) { - $key = "$($min)_$($p)" - $dmg = $arrDmgMin.$key - if ($kills -eq '' -or $kills -lt 1) { $kills = 0 } - - $table += "" - - $subtotalDmg[$count2] += $dmg - - if ($min -in $timeBlock,([math]::Floor($round1EndTime / 60))) { - #rnd total - if ($min -le ([math]::Floor($round1EndTime / 60))) { - $table += "" - } else { - $table += "" - } - } - - $count2 +=1 - } - - $table += "`n" - $count += 1 -} - -$table += '' - -$count = 0 -foreach ($st in $subtotalDmg) { - $table += "" - $count += 1 - - if ($count -in $timeBlock,([math]::Floor($round1EndTime / 60))) { $table += "" } -} -$table += "" - -$htmlOut += $tableHeader -$htmlOut += $table -$htmlOut += "
#PlayerTeamKillsDthTK" -#resue the previous table header. - -$table = '' -$subtotalDmg = @(1..$timeBlock | foreach { 0 } ) -$count = 1 - -$tableHeader = $tableHeader -replace '>Kills<','>Dmg<' - -foreach ($p in $playerList) { - $table += "
$($count)$($p)$($arrTeam.$p)$($arrDmgTotal.$p)$($arrDeath.$p)$($arrTKill.$p)$($dmg)$($arrDmgTotalRnd1.$p)$($arrDmgTotalRnd2.$p)
Total:$($subtotalDmg[$count])
" - - -### -# Flag Cap/Took/Drop per min -### - -$htmlOut += "

Per Minute - Flag stats

" - -$tableHeader = "$($tableStyle2) - -" } -$tableHeader += "" - -$count = 1 -$subtotalCap = @(1..$timeBlock | foreach { 0 } ) -$subtotalTook = @(1..$timeBlock | foreach { 0 } ) -$subtotalThrow = @(1..$timeBlock | foreach { 0 } ) - -foreach ($p in $playerList) { - $table += "" - - $count2 = 0 - foreach ($min in 1..$timeBlock) { - $key = "$($min)_$($p)" - $cap = $arrFlagCapMin.$key - #$took = $arrFlagTookMin.$key - $Took = $arrFlagTookMin.$key - $Throw = $arrFlagThrowMin.$key - $Stop = $arrFlagStopMin.$key - - if ($cap -in '',$null -and $took -in '',$null) { - if ($Stop -notin '',$null) { - $value = $arrFlagStopMin.$key - $cellCC = $ccAmber - } else { - $value = '' - $cellCC = nullValueColorCode - } - } else { - $subtotalCap[$count2] += $cap - if ($took -in '',$null) { $took = 0 } - $subtotalTook[$count2] += $Took - if ($throw -in '',$null) { $throw = 0 } - $subtotalThrow[$count2] += $Throw - $cellCC = $ccGreen - if ($cap -in '',$null) { $cap = '0'; $cellCC = $ccOrange } - $value = "$($cap)/$($Took)/$($throw)" - } - - $table += "" - $count2 +=1 - } - - $table += "`n" - $count += 1 -} - -$table += '' -$count = 0 -foreach ($st in $subtotalCap) { - $table += "" - $count += 1 -} -$table += "" - -$htmlOut += $tableHeader -$htmlOut += $table -$htmlOut += "
Rnd1 (Cp/Tk/Thr or Stop)Rnd2 (Cp/Tk/Thr or Stop)
#PlayerTeamCapsTookThrowTimeStop" -$table = '' - -foreach ($min in 1..$timeBlock) { $tableHeader += "$($min)
$($count)$($p)$($arrTeam.$p)$($arrFlagCap.$p)$($arrFlagTook.$p)$($arrFlagThrow.$p)$("{0:m\:ss}" -f [timespan]::FromSeconds($arrTimeFlag.$p))$($arrFlagStop.$p)$($value)
Total:$($subtotalCap[$count])/$($subtotalTook[$count])/$($subtotalThrow[$count])
" - -Remove-Variable cellCC,value,took,cap,key,subtotalCap,subtotalTook - -### -# Class related tables.... -### - -$count = 1 -$tableHeader = " - - - - - - - - - - - -" - -$table = '' -$subtotalFrg = @($ClassAllowedwithSG | foreach { 0 }) -$subtotalDth = @($ClassAllowedwithSG | foreach { 0 }) - -foreach ($p in $playerList) { - $table += "" - - $count2 = 0 - foreach ($o in ($ClassAllowedwithSG)) { - $key = "$($p)_$($o)" - $kills = $arrFragClass.$key - $dth = $arrDeathClass.$key - - if ($kills -eq '' -or $kills -lt 1) { $kills = 0 } - if ($dth -eq '' -or $dth -lt 1) { $dth = 0 } - - if ($kills + $dth -gt 0) { - $table += "" - } else { - $table += "" - } - - $subtotalFrg[$count2] += $kills - $subtotalDth[$count2] += $dth - $count2 +=1 - } - - $table += "`n" - $count += 1 -} - -$table += '' -$count = 0 -foreach ($st in $subtotalFrg) { $table += ""; $count++ } - - -#'
' | Out-File -FilePath $$htmlOut += -Append -$htmlOut += '
' -$htmlOut += '
' -$htmlOut += '

Kills/Deaths By Class

' -$htmlOut += $tableHeader -$htmlOut += $table -$htmlOut += '
#PlayerTeamKillsScoSoldDemoMedHwGPyroSpyEngSG
$($count)$($p)$($arrTeam.$p)$($arrFragTotal.$p)$($kills)/$($dth)
Total:$(if (0 -ne $subtotalFrg[$count] + $subtotalDth[$count]) { "$($subtotalFrg[$count])/$($subtotalDth[$count])" })
' -$htmlOut += '
' - - -$table = '' -$tableHeader = " - - - -" - -$count = 1 -foreach ($p in $playerList) { - - if ($arrDeathRnd1.$p -in $null,'') { $kd = 'n/a' } - else { $kd = [math]::Round($arrFragTotalRnd1.$p/$arrDeathRnd1.$p,2) } - - $table += "" - - $count2 = 1 - foreach ($o in $classAllowed) { - $key = "$($p)_$($o)" - - if ($arrTimeClassRnd1.$key -in '',$null -or $kd -eq 'n/a') { $time = '' } - else { $time = "{0:m\:ss}" -f [timespan]::FromSeconds($arrTimeClassRnd1.$key) } - - if ($time) { $table += "" } - else { $table += "" } - $count2 +=1 - } - - if ($arrDeathRnd2.$p -in $null,'') { $kd = 'n/a' } - else { $kd = [math]::Round($arrFragTotalRnd2.$p/$arrDeathRnd2.$p,2) } - $table += "" - - $count2 = 1 - foreach ($o in $classAllowed) { - $key = "$($p)_$($o)" - #$time = [math]::Round($arrTimeClass.$key / 60,1) - - if ($arrTimeClassRnd2.$key -in '',$null -or $kd -eq 'n/a') { $time = '' } - else { $time = "{0:m\:ss}" -f [timespan]::FromSeconds($arrTimeClassRnd2.$key) } - - if ($time) { $table += "" } - else { $table += "" } - $count2 +=1 - } - - $table += "`n" - $count += 1 -} - - -#'
' -$htmlOut += '

Estimated Time per Class

' -$htmlOut += $tableHeader -$htmlOut += $table -$htmlOut += '
Rnd1Rnd2
#PlayerTeamK/D -ScoSoldDemoMedHwGPyroSpyEngK/DScoSoldDemoMedHwGPyroSpyEng
$($count)$($p)$($arrTeam.$p)$($kd)$($time)$($kd)$($time)
' -$htmlOut += '
' - -#Stats for each player -$htmlOut += "

Player Weapon Stats

" - -$count = 1 -$table = '' -$tableHeader = '' -$tableHeader += "" - - -$allFragDeathKeys = @() -$allFragDeathKeys = $arrWeapFrag.Keys -$allFragDeathKeys += $arrWeapDeath.Keys -$allFragDeathKeys += $arrWeapDmgTaken.Keys - -$allWeapKeys = @() -foreach ($w in $allFragDeathKeys) { - $weap = $w -split '_' - $weap = "$($weap[1])_$($weap[2])" - - if ($weap -ne '' -and $weap -notin $allWeapKeys) { $allWeapKeys += $weap } -} - -$tmp = @() -$tmp = $allWeapKeys -notmatch '(_suicide|-ff$)' -match '^[1-9]_.*$'| Sort -$tmp += $allWeapKeys -notmatch '(_suicide|-ff$)' -match '^10_.*$'| Sort -#$tmp += '10_sentdmg' not working -$tmp += $allWeapKeys -notmatch '(_suicide|-ff$)' -match '^0_.*$'| Sort -$tmp += $allWeapKeys -match '_suicide$' | Sort -$tmp += $allWeapKeys -match '-ff$' | Sort -$allWeapKeys = $tmp -Remove-Variable tmp - -$divCol = 1 -$htmlOut += '
' -$htmlOut += '
' -$htmlOut += '

Team 1

' - -foreach ($p in $playerList) { - #$table += "
" - if ($divCol -eq 1 -and $arrTeam.$p -gt 1) { - $htmlOut += '
' - $htmlOut += '

Team 2

' - $divCol += 1 - } - - $htmlOut += "

$($p)

" - - $pClassKeys = @() - $totalTime = 0 - $classStats = '' - $pClassKeys = $arrTimeClassRnd1.Keys | foreach { if ($_ -match "^$($p -replace $regExReplaceFix)_[1-9]`$") { $_ } } - foreach ($i in $pClassKeys) { $totalTime += $arrTimeClassRnd1.$i } - foreach ($i in $pClassKeys) { $classStats += "$($ClassToStr[($i -split '_')[1]]) ($('{0:p0}' -f ($arrTimeClassRnd1.$i / $totalTime))) " } - - $htmlOut += "Rnd1: $($classStats) | " - - $pClassKeys = @() - $totalTime = 0 - $classStats = '' - $pClassKeys = $arrTimeClassRnd2.Keys | foreach { if ($_ -match "^$($p -replace $regExReplaceFix)_[1-9]`$") { $_ } } - - foreach ($i in $pClassKeys) { $totalTime += $arrTimeClassRnd2.$i } - foreach ($i in $pClassKeys) { $classStats += "$($ClassToStr[($i -split '_')[1]]) ($('{0:p0}' -f ($arrTimeClassRnd2.$i / $totalTime))) " } - - $htmlOut += "Rnd2: $($classStats)

" - Remove-Variable pClassKeys, totalTime, classStats - - $playerStats = '' - $lastClass = '' - $totKill = @(0,0,0) - $totDmg = @(0,0,0) - $totDth = @(0,0,0) - $totDmgTk = @(0,0,0) - $foundFF = 0 - - foreach ($w in $allWeapKeys) { - $key = "$($p)_$($w)" - - if ($arrWeapFrag.$key -gt 0 -or $arrWeapDeath.$key -gt 0 -or $arrWeapDmg.$key -gt 0 -or $arrWeapDmgTaken.$key -gt 0) { - $arrSplit = ($w -split '_') - #if ($arrSplit[0] -eq 10) { $class = $ClassToStr[9] } - #else { $class = $ClassToStr[$arrSplit[0]] } - $class = $ClassToStr[$arrSplit[0]] - - if ($class -ne $lastClass -and $foundFF -lt 1) { - if ($lastClass -ne '') { - #Do subtotal and reset total counter - $totKill = $totKill | foreach { if ($_ -eq 0) { '' } else { $_ } } - $totDmg = $totDmg | foreach { if ($_ -eq 0) { '' } else { $_ } } - $totDth = $totDth | foreach { if ($_ -eq 0) { '' } else { $_ } } - $totDmgTk = $totDmgTk | foreach { if ($_ -eq 0) { '' } else { $_ } } - $playerStats += "
" - } - - if ($w -match '-ff$' -or $w -match '_suicide$') { $foundFF++ } - - $lastClass = $class - $totKill = @(0,0,0) - $totDmg = @(0,0,0) - $totDth = @(0,0,0) - $totDmgTk = @(0,0,0) - } - - $totKill[0] += $arrWeapFragRnd1.$key; $totKill[1] += $arrWeapFragRnd2.$key; $totKill[2] += $arrWeapFrag.$key; - $totDmg[0] += $arrWeapDmgRnd1.$key; $totDmg[1] += $arrWeapDmgRnd2.$key; $totDmg[2] += $arrWeapDmg.$key; - $totDth[0] += $arrWeapDeathRnd1.$key; $totDth[1] += $arrWeapDeathRnd2.$key; $totDth[2] += $arrWeapDeath.$key; - $totDmgTk[0] += $arrWeapDmgTakenRnd1.$key; $totDmgTk[1] += $arrWeapDmgTakenRnd2.$key; $totDmgTk[2] += $arrWeapDmgTaken.$key; - - if ($arrSplit[1] -eq 'suicide' -or $w -match '.*-ff$') { - $ccNormOrSuicide = $ccAmber - } else { - $ccNormOrSuicide = $ccOrange - } - - $playerStats += " - - - " - } - } - - # Do the last total - Do I bother fixing same code in the loop? - $totKill = $totKill | foreach { if ($_ -eq 0) { '' } else { $_ } } - $totDmg = $totDmg | foreach { if ($_ -eq 0) { '' } else { $_ } } - $totDth = $totDth | foreach { if ($_ -eq 0) { '' } else { $_ } } - if ($foundFF) { $lastClass = 'Friendly' } - $playerStats += "" - - $htmlOut += $tableHeader - $htmlOut += $playerStats - $htmlOut += "
Rnd1Rnd2Total
WeaponClassKillsDmgDthDmgTKillsDmgDthDmgTKillsDmgDthDmgT
$($count)$($p)$($arrTeam.$p)
$($lastClass) Totals:$($totKill[0])$($totDmg[0])$($totDth[0])$($totDmgTk[0])$($totKill[1])$($totDmg[1])$($totDth[1])$($totDmgTk[1])$($totKill[2])$($totDmg[2])$($totDth[2])$($totDmgTk[2])
$($arrSplit[1])$($class)$($arrWeapFragRnd1.$key)$($arrWeapDmgRnd1.$key)$($arrWeapDeathRnd1.$key)$($arrWeapDmgTakenRnd1.$key)$($arrWeapFragRnd2.$key)$($arrWeapDmgRnd2.$key)$($arrWeapDeathRnd2.$key)$($arrWeapDmgTakenRnd2.$key)$($arrWeapFrag.$key) $($arrWeapDmg.$key) $($arrWeapDeath.$key)$($arrWeapDmgTaken.$key)
$($lastClass) Totals:$($totKill[0])$($totDmg[0])$($totDth[0])$($totDmgTk[0])$($totKill[1])$($totDmg[1])$($totDth[1])$($totDmgTk[1])$($totKill[2])$($totDmg[2])$($totDth[2])$($totDmgTk[0])
" - - - $count += 1 -} - -$htmlOut += '' -$htmlOut += "" - -$htmlOut | Out-File -FilePath $outFileStr - -& $outFileStr diff --git a/FO_stats_v2.ps1 b/FO_stats_v2.ps1 new file mode 100644 index 0000000..b2126fc --- /dev/null +++ b/FO_stats_v2.ps1 @@ -0,0 +1,2675 @@ +### +# PS COMMAND LINE:- & .\FO_stats_v2.ps1 -StatFile 'x:\path\filename.json' [-RoundTime ] [-TextOnly] [-TextSave] [-NoStatJson] +# WIN COMMAND LINE:- powershell -Command "& .\FO_stats_v2.ps1 -StatFile 'x:\path\filename.json' [-RountTime ] [-TextOnly] [-TextSave] [-NoStatJson]" +# +# NOTE: StatFile parameter now accepts *.json wildcard to generate many HTMLs, Text/Json stats are ALL STATS COMBINED. +# +# For individual TextJson Only stats for many stat files - i.e. not all games combined. +# PS *.JSON:- foreach ($f in (gci 'x:\stats\*.json')) { & .\FO_stats_v2.ps1 -StatFile ($f.ToString() -replace '\[','`[' -replace '\]','`]') -TextOnly } +### + +param ( + [Parameter(Mandatory = $true)] + [string[]]$StatFile, + [int] $RoundTime, + [switch]$TextSave, + [switch]$NoStatJson, + [switch]$TextOnly, + [switch]$OpenHTML +) + + +# Process input file, make sure it is valid. +$inputFileStr = $StatFile -replace '(? + +$ccGrey = 'cellGrey' +$ccAmber = 'cellAmber' +$ccOrange = 'cellOrange' +$ccGreen = 'cellGreen' +$ccBlue = 'rowTeam1' +$ccRed = 'rowTeam2' +$ccPink = 'rowTeamBoth' + + + +# 0 1 2 3 4 5 6 7 8 9 10 +$script:ClassToStr = @('World', 'Sco', 'Snp', 'Sold', 'Demo', 'Med', 'HwG', 'Pyro', 'Spy', 'Eng', 'SG') +$script:ClassAllowedStr = @('Sco', 'Sold', 'Demo', 'Med', 'HwG', 'Pyro', 'Spy', 'Eng') +$script:ClassAllowedWithSGStr = @('Sco', 'Sold', 'Demo', 'Med', 'HwG', 'Pyro', 'Spy', 'Eng', 'SG') +$script:ClassAllowed = @(1, 3, 4, 5, 6, 7, 8, 9) +$script:ClassAllowedwithSG = @(1, 3, 4, 5, 6, 7, 8, 9, 10) +$script:TeamToColor = @('Civ', 'Blue', 'Red', 'Yellow', 'Green') + +function getPlayerClasses { + param ($Round, $Player, $TimePlayed) + + $pos = arrFindPlayer -Table ([ref]$arrPlayerTable) -Player $Player -Round $Round + $filter = ($arrClassTimeTable | Where-Object { $_.Name -eq $Player -and ($Round -lt 1 -or $_.Round -eq $Round) }) + $classes = ($filter | % { $_.PSObject.Properties | Where Name -in $ClassAllowedStr | Where Value -gt 5 } | % { $_.Name }) -join ',' + + #$hover = ($classes -split ',' | % { "$_: $(Format-MinSec $filter.$_)" }) -join '
' + #return "$classes$hover " + return $classes +} + +function nullValueColorCode { + switch ($args[0]) { + '' { $ccGrey } + '0' { $ccGrey } + default { $args[1] } + } +} + +function nullValueAsBlank { + if ($args[0] -match '^0+%?$') { + return '' + } + else { + $args[0] + } +} + +function teamColorCode { + switch ($args[0]) { + '1' { 'rowTeam1' } + '2' { 'rowTeam2' } + default { 'rowTeamBoth' } + } +} + +function teamColorCodeByName { + foreach ($name in ($args[0] -split ',')) { + $tm = $arrteam.$name + if ($lasttm -and $lasttm -ne $tm) { return 'rowTeamBoth' } + $lasttm = $tm + } + return "rowTeam$tm" +} + +<## See fo_stats.css +#Friendly fire is bad +function actionColorCode ($arrTeam, $p1, $p2) { + #check if action is no Friendly fire + if ($p1 -eq $p2) { + $ccAmber #yellow + } elseif ($arrTeam.$p1 -eq $arrTeam.$p2) { + #Friendly fire + $ccOrange #orange + } else { + $ccGreen #green + } +}#> + + +function actionColorCode ($arrTeam, $p1, $p2) { + #check if action is no Friendly fire + if ($p1 -eq $p2) { + 'cellAmber' + } + elseif ($arrTeam.$p1 -eq $arrTeam.$p2) { + #Friendly fire + 'cellOrange' #orange + } + else { + 'cellGreen' #green + } +} + +function timeRnd-RoundDown { + # p1 = $time , $p2 = roundendtime + if ($args[0] / 60 -lt 0.4) { + if ($args[0] -lt $args[1]) { '0' } + else { $args[1] } + } + else { $time } +} + +function Format-MinSec { + param($sec) + if ($sec -eq $null) { return } + $ts = (New-TimeSpan -Seconds $sec) + $mins = ($ts.Days * 24 + $ts.Hours) * 60 + $ts.minutes + return "$($mins):$("{0:d2}" -f $ts.Seconds)" +} + +function Table-ClassInfo { + param([ref]$Table, $Name, $TimePlayed) + $out = '' + $classlist = @{} + foreach ($p in [array]$Table.Value) { + if ($p.Name -eq $Name) { + foreach ($class in $ClassAllowed) { + $strClass = $ClassToStr[$class] + $time = $p.($strClass) + + if ($time -notin 0, '', $null) { + $classlist.$strClass = ($time / $TimePlayed) + } + } + + foreach ($c in ($classlist.GetEnumerator() | Sort-Object Value -Descending)) { + $out += "$(($c.Name).PadRight(4)) $(('{0:P0}' -f $c.Value).PadLeft(3))|" + } + + return $out -replace '\|$', '' + } + } +} + +# Convert weapon names into friendly short names +function weapSN { + + switch ($args[0]) { + '' { 'NULL' } + #common + 'info_tfgoal' { 'laser' } + 'supershotgun' { 'ssg' } + 'shotgun' { 'sg' } + 'normalgrenade' { 'hgren' } + 'grentimer' { 'hgren' } + 'axe' { 'axe' } + 'spike' { 'ng' } + 'nailgun' { 'ng' } + + #7/2/23 - New inflictor/weaps?? + 'grenade grenadegrenade' { 'hgren' } + 'red grenadegrenade' { 'gl' } + 'mirv grenadegrenade' { 'mirv' } + 'mirvlet grenadegrenade' { 'mirv' } + 'napalm grenadegrenade' { 'napalm' } + 'shock grenadegrenade' { 'shock' } + 'emp grenadegrenade' { 'emp' } + 'flash grenadegrenade' { 'flash' } + + #23/2/23 - Newby changes v2 + 'incendiarylauncher' { 'incen' } + 'napalmgrenade' { 'napalm' } + 'glgrenade' { 'gl' } + + #scout + 'flashgrenade' { 'flash' } + + #sold + 'proj_rocket' { 'rl' } + 'rocketlauncher' { 'rl' } + 'shockgrenade' { 'shock' } + + #demo + 'detpack' { 'detp' } + 'pipebomb' { 'pipe' } + 'pipebomblauncher' { 'pipe' } + 'grenade' { 'gl' } + 'grenadelauncher' { 'gl' } + 'mirvsinglegrenade' { 'mirv' } + 'mirvgrenade' { 'mirv' } + + #medic + 'medikit' { 'bio' } + 'superspike' { 'sng' } + 'supernailgun' { 'sng' } + + #hwg + 'proj_bullet' { 'cann' } + 'assaultcannon' { 'cann' } + + #pyro + 'pyro_rocket' { 'incen' } + 'incendiary' { 'incen' } + 'flamethrower' { 'flame' } + 'fire' { 'fire' } + 'flamerflame' { 'flame' } + + #spy - knife + 'proj_tranq' { 'tranq' } + 'tranquilizer' { 'tranq' } + + #eng + 'spanner' { 'spann' } + 'empgrenade' { 'emp' } + 'ammobox' { 'emp' } + 'sentrygun' { 'sent' } + 'railslug' { 'rail' } + 'railgun' { 'rail' } + 'building_dispenser' { 'disp' } + 'building_sentrygun' { 'sent' } + 'build_timer' { 'sent' } + + #remove underscore to avoid token key issues. + default { $args[0] -replace '[_ ]', '-' } + } +} + +#Summary table funcitons +function attOrDef { + if ($args[0] -lt 1 -or $args[0] -gt 2) { return '' } + elseif ($args[0] -eq $args[1]) { return 'Att' } + else { return 'Def' } +} +function arrFindPlayer { + param( + [ref]$Table, + [string]$Player, + [int]$Round + ) + + process { + $count = 0 + foreach ($i in $Table.Value) { + if ($i.Name -eq $Player -and (!$Round -or $i.Round -eq $Round)) { + return $count + } + $count++ + } + return -1 + } +} + +function arrSummaryTable-UpdatePlayer { + param( + [ref]$table, + [string]$player, + [int]$kills, + [int]$death, + [int]$tkill + ) + + process { + $playerpos = (arrFindPlayer -Table $table -Player $player) + + #if player round update, else setup object + if ($playerpos -lt 0) { + $obj = [PSCustomObject]@{ + Name = $player + KPM = $null + KD = $null + Kills = 0 + Death = 0 + TKill = 0 + Dmg = 0 + DPM = $null + SGKills = 0 + SGDeath = 0 + FlagCap = 0 + FlagTake = 0 + FlagTime = 0 + FlagStop = 0 + Win = 0 + Draw = 0 + Loss = 0 + TimePlayed = 0 + Classes = '' + } + $playerpos = $table.Value.Length + $table.Value += $obj + } + + ($table.Value)[$playerpos].Kills += $kills + ($table.Value)[$playerpos].Death += $death + ($table.Value)[$playerpos].TKill += $tkill + } +} + +function arrSummaryTable-SetPlayerProperty { + param( + [ref]$table, + [string]$player, + [string]$property, + $value + ) + + process { + if ($Value -eq $null) { $Value = 1 } + $playerpos = (arrFindPlayer -Table $table -Player $player) + if ($playerpos -gt -1 -and $value -gt 0) { ($table.Value)[$playerpos].$property += $value } + } +} + +function arrClassTable-UpdatePlayer { + param( + [ref]$Table, + [string]$Player, + [string]$Class, + [int]$Round, + $Value + ) + + process { + if ($Class -in $ClassAllowed) { $Class = $ClassToStr[$Class] } + if ($Class -notin $ClassAllowedWithSGStr) { return } + + if ($round) { $playerpos = (arrFindPlayer -Table $Table -Player $Player -Round $round) } + else { $playerpos = (arrFindPlayer -Table $Table -Player $Player) } + + #Setup object if plyaer not found + if ($playerpos -lt 0) { + $obj = [PSCustomObject]@{ + Name = $Player + Round = 0 + Sco = 0 + Sold = 0 + Demo = 0 + Med = 0 + HwG = 0 + Pyro = 0 + Spy = 0 + Eng = 0 + SG = 0 + } + + if ($round) { $obj.Round = $round } + $playerpos = $table.Value.Length + $table.Value += $obj + } + $table.Value[$playerpos].$Class += $Value + } +} + +function arrClassTable-GetPlayerTotal { + param([pscustomobject]$player) + + return ($player.Sco + $player.Sold + $player.Demo + $player.Med + $player.Hwg + $player.Pyro + $player.Spy + $player.Eng + $player.SG) +} + +function arrClassTable-FindPlayerTotal { + param([string]$player, [int]$rnd) + + $pos = (arrFindPlayer -Table ([ref]$arrClassTimeTable) -Player $player -Round $rnd) + + return ($arrClassTimeTable[$pos].Sco + $arrClassTimeTable[$pos].Sold + $arrClassTimeTable[$pos].Demo ` + + $arrClassTimeTable[$pos].Med + $arrClassTimeTable[$pos].Hwg + $arrClassTimeTable[$pos].Pyro ` + + $arrClassTimeTable[$pos].Spy + $arrClassTimeTable[$pos].Eng + $arrClassTimeTable[$pos].SG) +} + +function arrFindPlayer-WeaponTable { + param( [string]$Name, $Round, $PlayerClass, [string]$Weapon, $Class ) + + $count = 0 + foreach ($p in $script:arrWeaponTable) { + if ($p.Name -eq $Name -and $p.Round -eq $Round -and ($PlayerClass -and $p.PlayerClass -eq $PlayerClass) -and $p.Weapon -eq $Weapon -and $p.Class -eq $Class) { + return $count + } + $count += 1 + } + return -1 +} + +function arrWeaponTable-UpdatePlayer { + param( [Parameter(Mandatory = $true)][string]$Name, + [Parameter(Mandatory = $true)][string]$PlayerClass, + [Parameter(Mandatory = $true)] $Round, + [Parameter(Mandatory = $true)][string]$Weapon, + [Parameter(Mandatory = $true)] $Class, + [Parameter(Mandatory = $true)][string]$Property, + $Value, + [switch]$Increment + ) + + $pos = arrFindPlayer-WeaponTable -Name $Name -Round $Round -Weapon $Weapon -Class $Class -PlayerClass $PlayerClass + if ($pos -lt 0) { + $obj = [pscustomobject]@{ Name = $Name + PlayerClass = $PlayerClass + Team = '' + Round = [int]$Round + Weapon = $Weapon + Class = [int]$Class + Kills = 0 + Death = 0 + Dmg = 0 + DmgTaken = 0 + AttackCount = 0 + DmgCount = 0 + } + + $script:arrWeaponTable += $obj + $pos = $script:arrWeaponTable.Length - 1 + } + + if ($Increment -and $Value -eq $null) { $Value = [int]1 } + if ($Increment) { $script:arrWeaponTable[$pos].$Property += [int]$Value } + else { $script:arrWeaponTable[$pos].$Property = $Value } +} + + +function arrPlayerTable-UpdatePlayer { + param( [Parameter(Mandatory = $true)][string]$Name, + [Parameter(Mandatory = $true)] $Round, + [Parameter(Mandatory = $true)][string]$Property, + $Value, + [switch]$Increment + ) + + $pos = arrFindPlayer -Table ([ref]$arrPlayerTable) -Player $Name -Round $Round + if ($pos -lt 0) { + $obj = $obj = [pscustomobject]@{ Name = $Name + Team = '' + Round = [int]$Round + Kills = 0 + SGKills = 0 + SGDeath = 0 + Death = 0 + TKill = 0 + Dmg = 0 + DmgTaken = 0 + DmgTeam = 0 + FlagCap = 0 + FlagDrop = 0 + FlagTake = 0 + FlagThrow = 0 + FlagTime = 0 + FlagStop = 0 + } + + $script:arrPlayerTable += $obj + $pos = $script:arrPlayerTable.Length - 1 + } + + if ($Increment -and $Value -eq $null) { $Value = [int]1 } + if ($Increment) { $script:arrPlayerTable[$pos].$Property += $Value } + else { $script:arrPlayerTable[$pos].$Property = $Value } +} +#end Summary table functions + +function GenerateVersusHtmlInnerTable { + param([ref]$VersusTable, $Player, $Round) + + switch ($Round) { + '1' { $refTeam = [ref]$arrTeamRnd1 } + '2' { $refTeam = [ref]$arrTeamRnd2 } + default { $refTeam = [ref]$arrTeam } + } + + $tbl = '' + $count2 = 0 + foreach ($o in $playerList) { + $key = "$($player)_$($o)" + $kills = $VersusTable.Value.$key + $killsOpponent = $VersusTable.Value."$($o)_$($player)" + if ($kills -eq '' -or $kills -lt 1) { $kills = 0 } + if ($killsOpponent -eq '' -or $killsOpponent -lt 1) { $killsOpponent = 0 } + + if ($o -eq $player) { + #$hoverText = "Self-affliction
$player`: $kills" + $colour = 'Amber' + } + elseif ($refTeam.Value.$Player -eq $refTeam.Value.$o) { + #$hoverText = "Friendly fire
$player`: $kills
$o`: $killsOpponent" + $colour = 'Orange' + } + else { + #$hoverText = "Head to Head
$player`: $kills
$o`: $killsOpponent" + $colour = 'Green' + } + + $tbl += "$($kills)" + # Java Scirpt implemented + #$tbl += "
$($kills)$hoverText
+ #" + if ($player -ne $o) { + $subtotal[$count2] = $kills + $subtotal[$count2] + } + $count2 += 1 + } + + return $tbl +} + +function GenerateSummaryHtmlTable { + param([switch]$Attack, [switch]$Defence) + + $count = 1 + $tableHeader = "" } + else { $tableHeader += "" } + $tableHeader += "" + $table = '' + $subtotal = @(1..9 | foreach { 0 }) + + foreach ($p in $playerList) { + if ($Defence) { + if ($arrTeam.$p -eq '1&2') { + $table += "" + $count += 1 + continue + } + elseif ($arrTeam.$p -eq '2&1') { + $rnds = @(1, 2) + } + else { + $rnds = if ($arrTeam.$p -match '^.*2$') { '1' } else { '2' } + } + } + else { + if ($arrTeam.$p -eq '2&1') { + $table += "" + $count += 1 + continue + } + elseif ($arrTeam.$p -eq '1&2') { + $rnds = @(1, 2) + } + else { + $rnds = if ($arrTeam.$p -match '^1.*$') { '1' } else { '2' } + } + } + + foreach ($rnd in $rnds) { + $player = ($arrPlayerTable | Where-Object { $_.Name -EQ $p -and (($_.Team -match '^1' -and $_.Round -eq $rnd) -or ($_.Team -match '^2' -and $_.Round -eq $rnd)) }) + if ($lastPlayer -ne $null -and $lastPlayer -eq $player.Name) { $count = $count - 1 } + + $team = (Get-Variable "arrTeamRnd$rnd").Value.$p + + if ($player -eq $null) { + $table += "" + if ($Attack) { $table += "" } + $table += "" + $count++ + continue + } + + $kills = $player.Kills + $timePlayed = arrClassTable-GetPlayerTotal ($arrClassTimeTable | Where-Object { $_.Name -eq $player.Name -and $_.Round -eq $player.Round }) + + $kpm = $kills / ($timePlayed / 60) + $death = $player.Death + $kd = if ($death) { $kills / $death } else { '-' } + $tkill = $player.TKill + $dmg = $player.Dmg + $dpm = $dmg / ($timePlayed / 60) + + $sgKilled = $player.SGKills + $sgDeath = $player.SGDeath + $flagCap = $player.FlagCap + $flagTake = $player.FlagTake + $flagTime = $player.FlagTime + $flagStop = $player.FlagStop + + $table += "" + + if ($Attack) { $table += "" } + else { $table += "" } + + $table += "" + #$table += "" + $table += "" + $table += "`n" + + $subtotal[0] += $kills; $subtotal[1] += $death; $subtotal[2] += $tkill; $subtotal[3] += $dmg; $subtotal[4] = '' + if ($Attack) { + $subtotal[5] += $sgKilled; $subtotal[6] += $flagCap; $subtotal[7] += $flagTake; $subtotal[8] += $FlagTime + } + else { + $subtotal[5] += $sgDeath; $subtotal[6] += $flagStop; $subtotal[7] = $null; $subtotal[8] = $null + } + $count += 1 + $lastPlayer = $player.Name + } + } + + $tableHeader += "`n" + $table += '' + + if ($Attack) { $subtotal[8] = Format-MinSec $subtotal[8] } + foreach ($st in $subtotal) { if ($st -eq $null) { break }; $table += "" } + $table += '' + + $ret = $tableHeader + $ret += $table + $ret += "
#PlayerTeamKPMK/DKillsDthTKDmgDPM" + if ($Attack) { $tableHeader += "SentCapsTakeCarrySentFlagStopTime
$($count)$($p)None
$($count)$($p)None
$($count)$($p)None
$($count)$($p)$($team)$('{0:0.00}' -f $kpm)$('{0:0.00}' -f $kd)$($kills)$($death)$($tkill)$($dmg)$('{0:0}' -f $dpm)$($sgkilled)$($flagCap)$($flagTake)$(Format-MinSec $flagTime)$($sgDeath)$($flagStop)$(Format-MinSec ([int]$timePlayed))
$(getPlayerClasses -Round $rnd -Player $p)
$(getPlayerClasses -Round $rnd -Player $p)
Classes
Total:$($st)
`n" + + return $ret +} + + +function GenerateFragHtmlTable { + param([string]$Round) + + switch ($Round) { + '1' { $refTeam = [ref]$arrTeamRnd1; $refVersus = [ref]$arrFragVersusRnd1 } + '2' { $refTeam = [ref]$arrTeamRnd2; $refVersus = [ref]$arrFragVersusRnd2 } + # Not used default { $refTeam = [ref]$arrTeam; $refVersus = [ref]$arrFragVersus } + } + + $count = 1 + $tableHeader = "" + $team = $refTeam.Value.$p + #$team = ($arrPlayerTable | Where { $_.Name -eq $p -and (!$Round -or $_.Round -eq $Round) } ` + # | %{ $_.Team} | Sort-Object -Unique) -join '&' + $kills = ($arrPlayerTable | Where { $_.Name -EQ $p -and (!$Round -or $_.Round -eq $Round) } | Measure-Object Kills -Sum).Sum + $death = ($arrPlayerTable | Where { $_.Name -EQ $p -and (!$Round -or $_.Round -eq $Round) } | Measure-Object Death -Sum).Sum + $tkill = ($arrPlayerTable | Where { $_.Name -EQ $p -and (!$Round -or $_.Round -eq $Round) } | Measure-Object TKill -Sum).Sum + + $table += "" + + $table += GenerateVersusHtmlInnerTable -VersusTable $refVersus -Player $p -Round $Round + + #$table += "" + $table += "" + $table += "`n" + + $count += 1 + } + + $tableHeader += "`n" + + $table += '' + foreach ($st in $subtotal) { $table += "" } + $table += '' + $ret = $tableHeader + $ret += $table + $ret += "
#PlayerTeamKillsDthTK" + $table = '' + $subtotal = @($playerList | foreach { 0 } ) + + foreach ($p in $playerList) { + $tableHeader += "$($count)
$($count)$($p)$($team)$($kills)$($death)$($tkill)
$(getPlayerClasses -Round $Round -Player $p)
$(getPlayerClasses -Round $Round -Player $p)
Classes
Total:$($st)
`n" + + return $ret +} + + +function GenerateDmgHtmlTable { + param([string]$Round) + + switch ($Round) { + '1' { $refTeam = [ref]$arrTeamRnd1; $refVersus = [ref]$arrDmgVersusRnd1 } + '2' { $refTeam = [ref]$arrTeamRnd2; $refVersus = [ref]$arrDmgVersusRnd2 } + #Not used default { $refTeam = [ref]$arrTeam; $refVersus = [ref]$arrDmgVersus } + } + + $count = 1 + $tableHeader = "" + $table = '' + $subtotal = @($playerList | foreach { 0 } ) + + foreach ($p in $playerList) { + $tableHeader += "" + $dmg = ($arrPlayerTable | Where { $_.Name -EQ $p -and (!$Round -or $_.Round -eq $Round) } | Measure-Object Dmg -Sum).Sum + $table += "" + + $table += GenerateVersusHtmlInnerTable -VersusTable $refVersus -Player $p -Round $Round + + #$table += "" + $table += "" + $table += "`n" + + $count += 1 + } + + $tableHeader += "`n" + + $table += '' + foreach ($st in $subtotal) { $table += "" } + $table += '' + $ret += $tableHeader + $ret += $table + $ret += "
#PlayerTeamDmg$($count)
$($count)$($p)$($refTeam.Value.$p)$($dmg)
$(getPlayerClasses -Round $Round -Player $p)
$(getPlayerClasses -Round $Round -Player $p)
Classes
Total: *minus self-dmg$($st)
`n" + return $ret +} + +#Text Based stats initialized - not reset after each file +$script:arrSummaryAttTable = @() +$script:arrSummaryDefTable = @() +$script:arrClassTimeAttTable = @() +$script:arrClassTimeDefTable = @() +$script:arrClassFragAttTable = @() +$script:arrClassFragDefTable = @() +$script:arrResultTable = @() + +$jsonFileCount = 0 +foreach ($jsonFile in $inputFile) { + # Enure JSON files with [ at start and ] (not added in log files) + $txt = (Get-Content ($jsonFile.FullName -replace '\[', '`[' -replace '\]', '`]')) + + if ($txt[0] -notmatch '^\[.*') { + $txt[0] = "[$($txt[0])" + $txt[$txt.count - 1] = "$($txt[$txt.count - 1])]" + $txt | Out-File -LiteralPath ($jsonFile.FullName) -Encoding utf8 + } + Remove-Variable txt + + if (!($jsonFile.Exists)) { Write-Host "ERROR: File not found - $($jsonFile.FullName)"; return } + + # Out file with same-name.html - remove pesky [] braces. + $outFileStr = ($jsonFile.FullName) -replace '\.json$', '' #-replace '`?(\[|\])','') + $json = ((Get-Content -Path ($jsonFile.FullName -replace '\[', '`[' -replace '\]', '`]') -Raw) | ConvertFrom-Json) + $jsonFileCount++ + Write-Host "Input File$(if ($inputFile.Length -gt 1) { " ($jsonFileCount/$($inputFile.Length))" } ): $($jsonFile.Name)" + Write-Host "$(Get-Date) | #1 Processing JSON" + + #Check for end round time (seconds) - default to 600secs (10mins) + if ($RoundTime -is [int] -and $RoundTime -gt 0) { $round1EndTime = $RoundTime } + else { $script:round1EndTime = 600 } + + # Leaving as HashTable, used for HTML display only + #Not used $script:arrFragVersus = @{} + $script:arrFragVersusRnd1 = @{} + $script:arrFragVersusRnd2 = @{} + #Not used $script:arrDmgVersus = @{} + $script:arrDmgVersusRnd1 = @{} + $script:arrDmgVersusRnd2 = @{} + + # Used for some HTML->Awards only + #Not used $script:arrKilledClass = @{} + $script:arrKilledClassRnd1 = @{} + $script:arrKilledClassRnd2 = @{} + + #Json parsing helper - _currentClass and _lastChange + $script:arrTimeTrack = @{} + + # Tracking teams via below and then updating arrPlayerTable after JSON parsing -Worth changing? + $script:arrTeam = @{} + $script:arrTeamRnd1 = @{} + $script:arrTeamRnd2 = @{} + + $script:arrResult = @{} + + # Leaving as HashTable, used for HTML display only + $script:arrFragMin = @{} + $script:arrDmgMin = @{} + $script:arrDeathMin = @{} + $script:arrFlagCapMin = @{} + $script:arrFlagDropMin = @{} + $script:arrFlagTookMin = @{} + $script:arrFlagThrowMin = @{} + $script:arrFlagStopMin = @{} + + # Table Arrays - PS Format-Table friendly + $script:arrAttackDmgTracker = @{} #Json parsing helper (AttackCount + Dmg Count) + $script:arrWeaponTable = @() # Filtering on PlayerClass for Class Kills, use measure-object + $script:arrPlayerTable = @() + $script:arrClassTimeTable = @() + + ### + # Process the JSON into above arrays (created 'Script' to be readable by all functions) + # keys: Frags/playername, Versus.player_enemy, Classes/player_class#, Weapons/player_weapon + ### + $script:round = 1 + $script:timeBlock = 1 + $prevItem = '' + + ForEach ($item in $json) { + $type = $item.type + $kind = $item.kind + + #Remove any underscores for _ tokens used in Keys + $player = $item.player -replace '[_,]', '.' -replace '\s', '.' -replace '\$', '§' -replace '\^([b0-9]{0,1}|&[0-9a-fA-F]{2}|x[0-9]{3})', '' -replace '\*','°' + $target = $item.target -replace '[_,]', '.' -replace '\s', '.' -replace '\$', '§' -replace '\^([b0-9]{0,1}|&[0-9a-fA-F]{2}|x[0-9]{3})', '' -replace '\*','°' + $prevPlayer = $prevItem.player -replace '[_,]', '.' -replace '\s', '.' -replace '\$', '§' -replace '\^([b0-9]{0,1}|&[0-9a-fA-F]{2}|x[0-9]{3})', '' -replace '\*','°' + $prevAttacker = $prevItem.attacker -replace '[_,]', '.' -replace '\s', '.' -replace '\$', '§' -replace '\^([b0-9]{0,1}|&[0-9a-fA-F]{2}|x[0-9]{3})', '' -replace '\*','°' + + $p_team = $item.playerTeam + $t_team = $item.targetTeam + $class = $item.playerClass + $classNoSG = $class -replace '10', '9' + $t_class = $item.targetClass + $dmg = [math]::Round($item.damage, 0) + #Shorten and fix multiple names on weapons + $weap = (weapSN $item.inflictor) + + #Setup time blocks for per minute scoring + $time = $item.time + if ($time -notin '', $null -and [math]::Ceiling($time / 60) -gt $timeBlock) { + #new time block found, update the time block + $timeBlock = [math]::Ceiling($time / 60) + } + + ### + # Fix Stupid stuff missing from logs due to 3rd Party events - e.g. Buildings and Gas + ### + + if ($round -eq 1) { $teamRound = [ref]$arrTeamRnd1 } + else { $teamRound = [ref]$arrTeamRnd2 } + + #try fix building kills/deaths + if ($t_class -eq 0 -and $target -in '', 'build.timer' -and $weap -ne 'worldSpawn') { + $potentialEng = $arrTimeTrack.keys -match '.*_currentClass$' + $potentialEng = $potentialEng | foreach { if ($arrTimeTrack.$_ -eq 9) { ($_ -split '_')[0] } } + $potentialEng = $potentialEng | foreach { if ($teamRound.Value.$_ -eq $t_team) { $_ } } + + # If only 1 eng found fix it, else forget it + if ($potentialEng -ne $null -and $potentialEng.Count -eq 1 ) { + $target = ($potentialEng -split '_')[0] + $t_class = 10 + } else { continue } + } + elseif ($weap -eq 'sent') { $class = 10 } + # Do this before Keys are made# dodgey... Try find out who a gas grenade owner is + elseif ($class -eq '8' -and $weap -eq 'worldspawn') { + if ($type -in 'damageDone', 'kill') { + $potentialSpies = $arrTimeTrack.keys -match '.*_currentClass$' + $potentialSpies = $potentialSpies | foreach { if ($arrTimeTrack.$_ -eq 8 -and $teamRound.Value.$_ -eq $team) { ($_ -split '_')[0] } } + + # If only 1 spy found fix it, else forget it + if ($potentialSpies.Count -eq 1 -and $potentialSpies -notin '', $null) { + $player = ($potentialSpies -split '_')[0] + $weap = 'gas' + } + else { continue } + } + } + + # add -ff to weap for friendly-fire + if ($p_team -and $t_team -and $p_team -eq $t_team -and $weap -ne 'laser') { $weap += '-ff' } + + # change weapon to suidcide for self kills + if ($player -and $player -eq $target) { $weap = 'suicide' } + + $key = "$($player)_$($target)" + $keyTime = "$($timeBlock)_$($player)" + #$keyTimeT = "$($timeBlock)_$($target)" + $keyClassK = "$($player)_$($class)_$($t_class)" + $keyWeap = "$($player)_$($class)_$($weap)" + + + # 19/12/21 New Attack/DmgDone stats in object/array format for PS Tables. + if ($type -in 'attack', 'damageDone' -and $player -and $weap -and $class -gt 0 ` + -and ($type -eq 'attack' -or $p_team -ne $t_team -or $player -ne $target)) { + + switch ($type) { + 'attack' { + if ($arrAttackDmgTracker.$keyWeap -eq -1) { + # damageDone registered before attack + $arrAttackDmgTracker.Remove($keyWeap) + } + elseif ($arrAttackDmgTracker.$keyWeap -gt 0) { + # attack registered - no dmg done found since + $arrAttackDmgTracker.Remove($keyWeap) + } + arrWeaponTable-UpdatePlayer -Name $player -PlayerClass $class -Round $round -Weapon $weap -Class $class -Property 'AttackCount' -Increment + } + 'damageDone' { + <# To avoid multi-hits: No item existing = No DmgCount added #> + if ($p_team -ne $t_team) { + if (!$arrAttackDmgTracker.$keyWeap) { + #Damage not registered, no attack yet found + $arrAttackDmgTracker.$keyWeap = -1 + arrWeaponTable-UpdatePlayer -Name $player -PlayerClass $class -Round $round -Weapon $weap -Class $class -Property 'DmgCount' -Increment + } + elseif (!$arrAttackDmgTracker.$keyWeap -gt 0) { + #Attack has been registered prior to damangeDone + arrWeaponTable-UpdatePlayer -Name $player -PlayerClass $class -Round $round -Weapon $weap -Class $class -Property 'DmgCount' -Increment + $arrAttackDmgTracker.Remove($keyWeap) + } + } + } + } + } + + #Round tracking + #if (($class -eq '0' -and $player -eq 'world' -and $p_team -eq '0' -and $weap -eq 'worldspawn' -and $time -ge $round1EndTime) -and $round -le 1) { #(($kind -eq 'enemy' -and ..) + if ($time -gt $round1EndTime -and $round -lt 2) { + $round += 1 + $prevItem = '-1' + + if ($arrTrackTime.flagPlayer -notin $null, '') { + arrPlayerTable-UpdatePlayer -Name $arrTrackTime.flagPlayer -Round $round -Property 'FlagTime' -Value ($round1EndTime - $arrTimeTrack.flagTook) -Increment + $arrTrackTime.flagTook = 0 + $arrTrackTime.flagPlayer = '' + } + + #Finalise Rnd1 Class times from the tracker. + foreach ($p in $arrTeam.Keys) { + if ($arrTimeTrack."$($p)_currentClass" -in '',$null) { continue } + + $lastChangeDiff = $round1EndTime - $arrTimeTrack."$($p)_lastChange" + arrClassTable-UpdatePlayer -Table ([ref]$arrClassTimeTable) -Player $p -Class $arrTimeTrack."$($p)_currentClass" -Round 1 ` + -Value ($lastChangeDiff) -Increment + + if ($lastChangeDiff -le 20) { + $arrTimeTrack."$($p)_endClassRnd1" = $arrTimeTrack."$($p)_previousClass" + } else { $arrTimeTrack."$($p)_endClassRnd1" = $arrTimeTrack."$($p)_currentClass" } + + $arrTimeTrack."$($p)_lastChange" = $round1EndTime + } + } + else { + if ($type -eq 'playerStart') { + $arrTimeTrack."$($player)_currentClass" = '-1' + $arrTimeTrack."$($player)_previousClass" = '-1' + $arrTimeTrack."$($player)_lastChange" = $time + } #elseif ($type -eq 'changeClass' -and $item.nextClass -ne 0) { continue } + + # Class tracking - Player and Target + foreach ($pc in @(@($player, $classNoSG), @($target, $t_class -replace '10', '9'))) { + if ($pc[0] -match '^(\s)*$') { continue } + + if ($round -eq 2 -and $arrTimeTrack."$($pc[0])_lastChange" -in '',$null -and $time -lt ($round1EndTime+20)) { + $arrTimeTrack."$($pc[0])_previousClass" = $arrTimeTrack."$($pc[0])_currentClass" + $arrTimeTrack."$($pc[0])_currentClass" = '-1' + $arrTimeTrack."$($pc[0])_lastChange" = $round1EndTime + } + + #This is making Rnd1 class bleed to Rnd2... + #if ($type -eq 'changeClass') { $currentClass = $class; $class = $item.nextClass; $class; $currentClass } + $lastChange = $arrTimeTrack."$($pc[0])_lastChange" + if ($lastChange -in '', $null) { + if ((($round1EndTime * $round) - $round1EndTime + $time) -lt 30) { + $lastChange = ($round1EndTime * $round) - $round1EndTime + } + else { $lastChange = $time } + } + + $lastChangeDiff = $time - $lastChange + + $currentClass = $arrTimeTrack."$($pc[0])_currentClass" + if ($type -eq 'changeClass') { $newClass = $item.nextClass; $oldClass = $pc[1] } + else { $newClass = $pc[1] ; $oldClass = $currentClass } + + if ($pc[1] -in $ClassAllowed -and $currentClass -ne $newClass) { + if ($currentClass -match '^(\s*|-1)$') { $currentClass = $pc[1] } + + #Record time + if ($lastChangeDiff -gt 0) { + arrClassTable-UpdatePlayer -Table ([ref]$arrClassTimeTable) -Player $pc[0] -Class $currentClass -Round $round -Value $lastChangeDiff -Increment + } + + #Update tracker after stuff is tallied + $arrTimeTrack."$($pc[0])_previousClass" = $oldClass + $arrTimeTrack."$($pc[0])_currentClass" = $newClass + $arrTimeTrack."$($pc[0])_lastChange" = $time + } + + if ($type -eq 'changeClass' -and $newClass -eq 0) { + $arrTimeTrack."$($pc[0])_previousClass" = $oldClass + $arrTimeTrack."$($pc[0])_currentClass" = '' + $arrTimeTrack."$($pc[0])_lastChange" = '' + } + } + } + + # Switch #1 - Tracking Goal/TeamScores prior to world/error checks skip loop. + # - Tracking all-death prior to this also (death due to fall/other damage). + switch ($type) { + # Not used 'gameStart' { $map = $item.map } + + 'goal' { + # arrTimeTrack.flag* updated under the fumble event + arrPlayerTable-UpdatePlayer -Name $arrTimeTrack.flagPlayer -Round $round -Property 'FlagTime' -Value ($time - $arrTimeTrack.flagTook) -Increment + arrPlayerTable-UpdatePlayer -Name $player -Round $round -Property 'FlagCap' -Increment + $arrFlagCapMin.$keyTime += 1 + } + + 'teamScores' { + # For the final team score message + $arrResult.team1Score = $item.team1Score + $arrResult.team2Score = $item.team2Score + $arrResult.winningTeam = $item.winningTeam + $arrResult.time = $time + + # Note - Add +10 points when Team1 wins to avoid a Draw being compared to a Win. + switch ($arrResult.winningTeam) { + '0' { $arrResult.winRating = 0; $arrResult.winRatingDesc = 'Nobody wins' } + '1' { $arrResult.winRating = 1 - ($arrResult.team2Score / ($arrResult.team1Score + 10)); $arrResult.winRatingDesc = "Wins by $($item.team1Score - $item.team2Score) points" } + default { $arrResult.winRating = (($round1EndTime * 2) - $arrResult.time) / $round1EndTime; $arrResult.winRatingDesc = "$("{0:m\:ss}" -f ([timespan]::fromseconds(($round1EndTime * 2) - $arrResult.time))) mins left" } + } + } + + 'death' { + $arrDeathMin.$keyTime += 1 + if ($player -ne '' -and $class -ne 0) { + arrPlayerTable-UpdatePlayer -Name $player -Round $round -Property 'Death' -Increment + + #Record death from the world (e.g. fall dmg) + if ($item.attacker -in 'world', '' -and !$item.attackerClass) { + $weap = 'world' + arrWeaponTable-UpdatePlayer -Name $player -PlayerClass $class -Round $round -Weapon $weap -Class $classNoSG -Property 'Death' -Increment + } + } + continue + } + } + + #Skip environment/world events for kills/dmg stats (or after this), let laser and team scores pass thru + if ((($player -eq '') -Or ($p_team -eq '0') -or ($t_team -eq '0') -or ($t_class -eq '0') -or ($weap -eq 'worldSpawn')) -and ($type -ne 'teamScores' -and $weap -ne 'laser')) { continue } + + + #team tracking + if ($p_team -notin $null, '' -and $p_team -gt 0 -and $class -gt 0 -and $type -and $weap -notlike 'worldspawn*') { + if ($player -eq 'world') { $weap; $item } + if ($arrTeam.$player -in '', $null) { + #Initialise team info when null + $arrTeam.$player = "$p_team" + } + elseif ($p_team -notin ($arrTeam.$player -split '&')) { + #Else if team not in list add it + $arrTeam.$player = "$($arrTeam.$player)&$($p_team)" + } + + #Do the same for Rnd1 / Rnd2 + switch ($round) { + '1' { + if ($arrTeamRnd1.$player -in '', $null) { + #Initialise team info when null + $arrTeamRnd1.$player = "$p_team" + } + elseif ($p_team -notin ($arrTeamRnd1.$player -split '&')) { + #Else if team not in list add it + $arrTeamRnd1.$player = "$($arrTeamRnd1.$player)&$($p_team)" + } + } + default { + if ($arrTeamRnd2.$player -in '', $null) { + #Initialise team info when null + $arrTeamRnd2.$player = "$p_team" + } + elseif ($p_team -notin ($arrTeamRnd2.$player -split '&')) { + #Else if team not in list add it + $arrTeamRnd2.$player = "$($arrTeamRnd2.$player)&$($p_team)" + } + + } + } + } + + # Switch #2 + # Frag and damage counter, + flags + scores + result + weap kills/deaths + switch ($type) { + 'kill' { + if ($player -ne $target -and $player -notin $null, '') { + #make sure player did not killed himself and has a name + if ($p_team -eq $t_team) { + #team kill recorded, not a normal kill + arrPlayerTable-UpdatePlayer -Name $player -Round $round -Property 'TKill' -Increment + } + else { + #Record the normal kill + arrPlayerTable-UpdatePlayer -Name $player -Round $round -Property 'Kills' -Increment + arrWeaponTable-UpdatePlayer -Name $player -PlayerClass $class -Round $round -Weapon $weap -Class $class -Property 'Kills' -Increment + + if ($t_class -eq 10) { + arrPlayerTable-UpdatePlayer -Name $player -Round $round -Property 'SGKills' -Increment + arrPlayerTable-UpdatePlayer -Name $target -Round $round -Property 'SGDeath' -Increment + } + #Not used $arrKilledClass.$keyClassK += 1 + $arrFragMin.$keyTime += 1 + + switch ($round) { + '1' { $arrKilledClassRnd1.$keyClassK += 1 } + default { $arrKilledClassRnd2.$keyClassK += 1 } + } + } + } + + #track all weap deaths on targets AND all versus kills (to see self/team kills in table). Exclude sentry death for player totals. + #dont track SG deaths except in the class and weapons stats. + if ($t_class -ne '10') { + if ($player -notin $null, '') { + #Not used $arrFragVersus.$key += 1 + switch ($round) { + '1' { $arrFragVersusRnd1.$key += 1 } + default { $arrFragVersusRnd2.$key += 1 } + } + + arrWeaponTable-UpdatePlayer -Name $target -PlayerClass $t_class -Round $round -Class $class -Weapon $weap -Property 'Death' -Increment + } + } + } + + 'damageDone' { + if ($player -ne $target) { + #make sure player did not hurt himself, not record in totals - versus only. + if ($p_team -ne $t_team) { + #track enemy damage only in the total + arrPlayerTable-UpdatePlayer -Name $player -Round $round -Property 'Dmg' -Value $dmg -Increment + arrPlayerTable-UpdatePlayer -Name $target -Round $round -Property 'DmgTaken' -Value $dmg -Increment + arrWeaponTable-UpdatePlayer -Name $player -PlayerClass $class -Round $round -Weapon $weap -Class $class -Property 'Dmg' -Value $dmg -Increment + arrWeaponTable-UpdatePlayer -Name $target -PlayerClass $t_class -Round $round -Weapon $weap -Class $class -Property 'DmgTaken' -Value $dmg -Increment + + $arrDmgMin.$keyTime += $dmg + } + elseif ($player) { + #team dmg + arrPlayerTable-UpdatePlayer -Name $player -Round $round -Property 'DmgTeam' -Value $dmg -Increment + } + } + #record all damage including self/team in versus table + #Not used $arrDmgVersus.$key += $dmg + switch ($round) { + '1' { $arrDmgVersusRnd1.$key += $dmg } + default { $arrDmgVersusRnd2.$key += $dmg } + } + } + + 'fumble' { + arrPlayerTable-UpdatePlayer -Name $arrTimeTrack.flagPlayer -Round $round -Property 'FlagTime' -Value ($time - $arrTimeTrack.flagTook) -Increment + $arrTimeTrack.'flagPlayer' = '' + $arrTimeTrack.'flagTook' = 0 + + arrPlayerTable-UpdatePlayer -Name $player -Round $round -Property 'FlagDrop' -Increment + $arrFlagDropMin.$keyTime += 1 + + # work out if death or throw + if ($prevAttacker -and $item.team -ne $prevItem.attackerTeam -and $prevItem.type -eq 'death' -and $prevPlayer -eq $player -and + $prevItem.time -eq $time -and $prevItem.kind -ne 'self') { + arrPlayerTable-UpdatePlayer -Name $prevAttacker -Round $round -Property 'FlagStop' -Increment + $arrFlagStopMin."$($timeBlock)_$($prevAttacker)" += 1 + } + elseif ($prevItem.kind -ne 'self') { + arrPlayerTable-UpdatePlayer -Name $player -Round $round -Property 'FlagThrow' -Increment + $arrFlagThrowMin.$keyTime += 1 + } + } + + 'pickup' { + $arrTimeTrack.flagTook = $time + $arrTimeTrack.flagPlayer = $player + arrPlayerTable-UpdatePlayer -Name $player -Round $round -Property 'FlagTake' -Increment + $arrFlagTookMin.$keyTime += 1 + } + } #end type switch + + if ($prevItem -ne '-1') { $prevItem = $item } + else { $prevItem = '' } + }#end for - finished parsing the JSON file + + #Close the arrTimeTrack flag + class stats a + $arrTimeTrack.flagPlayer = '' + $arrTimeTrack.flagTook = 0 + + foreach ($p in $arrTeamRnd2.Keys) { + $currentClass = $arrTimeTrack."$($p)_currentClass" + + if ($currentClass -in '',$null) { continue } + arrClassTable-UpdatePlayer -Table ([ref]$arrClassTimeTable) -Player $p -Class $currentClass -Round $round -Value ($time - $arrTimeTrack."$($p)_lastChange") + } + + #remove any Class Times where timed played less that 20secs + function arrClassTimeTable-Cleanup { + param([ref]$Table) + + foreach ($p in $Table.Value) { + $name = $p.Name + $totalTime = ($p | Measure $ClassAllowedStr -Sum).Sum + foreach ($c in $ClassAllowed) { + $cStr = $ClassToStr[$c] + $kills = ($arrWeaponTable | Where-Object { $_.Name -eq $p.Name -and $_.PlayerClass -eq $c -and $_.Round -eq $p.Round } | Measure-Object Kills -Sum).Sum + $dmg = ($arrWeaponTable | Where-Object { $_.Name -eq $p.Name -and $_.PlayerClass -eq $c -and $_.Round -eq $p.Round } | Measure-Object Dmg -Sum).Sum + + if ($p.$cStr -in 1..20 -and $kills -lt 1 -and $dmg -lt 1) { + + if ($p.Round -eq 1) { $p."$($ClassToStr[$arrTimeTrack."$($name)_endClassRnd1"])" += $p.$cStr } + else { $p."$($ClassToStr[$arrTimeTrack."$($name)_previousClass"])" += $p.$cStr } + $p.$cStr = 0 + } + elseif ($p.$cStr -gt $round1Endtime) { + $p.$cStr = $round1EndTime + } + } + } + } + + #cleanup class times + arrClassTimeTable-Cleanup ([ref]$arrClassTimeTable) + + ###### + #Create Ordered Player List + ##### + $playerList = ($arrTeam.GetEnumerator() | Sort-Object -Property Value, Name).Key + + ####### + # Add Team Info to tables and sort by Round/Team/Name + ###### + + foreach ($i in $arrWeaponTable) { + switch ($i.Round) { + 1 { $i.Team = $arrTeamRnd1.($i.Name) } + 2 { $i.Team = $arrTeamRnd2.($i.Name) } + default { $i.Team = $arrTeam.($i.Name) } + } + } + $arrWeaponTable = $arrWeaponTable | Sort-Object Round, Team, Name + + foreach ($i in $arrPlayerTable) { + switch ($i.Round) { + 1 { $i.Team = $arrTeamRnd1.($i.Name) } + 2 { $i.Team = $arrTeamRnd2.($i.Name) } + default { $i.Team = $arrTeam.($i.Name) } + } + } + $arrPlayerTable = $arrPlayerTable | Sort-Object Round, Team, Name + + if (!$TextOnly) { + ### + # Calculate awards + ## + Write-Host "$(Get-Date) | #2 Award calcualtions" + #create variables here, min/max values to be generated for awardAtt* + awardDef* (exclude *versus) + Remove-Variable -Name award* + $script:awardAttKills = @{} + $script:awardAttDeath = @{} + $script:awardAttDmg = @{} + $script:awardAttDmgTaken = @{} + $script:awardAttDmgTeam = @{} + $script:awardAttKD = @{} + $script:awardAttTKill = @{} + $script:awardAttDmgPerKill = @{} + $script:awardAttKillsVersus = @{} + $script:awardAttFlagCap = @{} + $script:awardAttFlagTook = @{} + $script:awardAttFlagTime = @{} + + $script:awardDefKills = @{} + $script:awardDefDeath = @{} + $script:awardDefDmg = @{} + $script:awardDefDmgTaken = @{} + $script:awardDefDmgTeam = @{} + $script:awardDefKD = @{} + $script:awardDefTKill = @{} + $script:awardDefDmgPerKill = @{} + $script:awardDefKillsVersus = @{} + + function awardScaler { + if ($arrResult.WinningTeam -eq 2) { + $playedPercent = ($arrResult.time - $round1EndTime) / $round1EndTime + return [math]::Floor($args[0] / $playedPercent) + } + else { return $args[0] } + } + + #Attack - Rnd1=T1 and Rnd2=T2 - Get Player list and get required Data sets + # Teams sorted in order, i.e. 1&2 = Att 2x, 2&1 = Def 2x. + $script:playerListAttRnd1 = ($arrTeamRnd1.Keys | foreach { if ($arrTeamRnd1.$_ -match '^(1|1&2)$' -and (arrClassTable-FindPlayerTotal $_ 1) -gt $round1EndTime - 60) { $_ } }) + $script:playerListAttRnd2 = ($arrTeamRnd2.Keys | foreach { if ($arrTeamRnd2.$_ -match '^(2|1&2)$' -and (arrClassTable-FindPlayerTotal $_ 2) -gt ($arrResult.time - $round1EndTime - 60)) { $_ } }) + + + ## Generate Attack/Def Tables, e.g. for att Rnd1 = Team1 attack + Rnd2 = Team2 attack + $count = 1 + + foreach ($array in @($playerListAttRnd1, $playerListAttRnd2)) { + foreach ($p in $array) { + #disqualify a player if they were on multiple teams + if ($arrTeam.$p -notmatch '^(1|2)$') { continue } + + if ($arrResult.WinningTeam -eq 2) { + $scaler = 1 / $arrResult.winRating + } + else { $scaler = 1 } + $pos = arrFindPlayer -Table ([ref]$arrPlayerTable) -Player $p -Round $count + + $awardAttFlagCap.Add( $p, $arrPlayerTable[$pos].FlagCap) + $awardAttFlagTook.Add($p, $arrPlayerTable[$pos].FlagTake) + $awardAttFlagTime.Add($p, $arrPlayerTable[$pos].FlagTime) + + + if ($count -eq 1) { + $awardAttKills.Add($p, $arrPlayerTable[$pos].Kills) + $awardAttDeath.Add($p, $arrPlayerTable[$pos].Death ) + $awardAttDmg.Add( $p, $arrPlayerTable[$pos].Dmg ) + $awardAttDmgTaken.Add($p, $arrPlayerTable[$pos].DmgTaken) + $awardAttDmgTeam.Add( $p, $arrPlayerTable[$pos].DmgTeam) + $awardAttTkill.Add($p, $arrPlayerTable[$pos].TKill) + $awardAttKD.Add( $p, ($arrPlayerTable[$pos].Kills - $arrPlayerTable[$pos].Death) ) + } + else { + $awardAttKills.Add($p, (awardScaler $arrPlayerTable[$pos].Kills )) + $awardAttDeath.Add($p, (awardScaler $arrPlayerTable[$pos].Death)) + $awardAttDmg.Add( $p, (awardScaler $arrPlayerTable[$pos].Dmg)) + $awardAttDmgTaken.Add( $p, (awardScaler $arrPlayerTable[$pos].DmgTaken)) + $awardAttDmgTeam.Add( $p, (awardScaler $arrPlayerTable[$pos].DmgTeam)) + $awardAttTkill.Add($p, (awardScaler $arrPlayerTable[$pos].TKill)) + + $awardAttKD.Add( $p, (awardScaler ($arrPlayerTable[$pos].Kills - $arrPlayerTable[$pos].Death)) ) + } + if ($arrPlayerTable[$pos].Kills -notin $null, '', '0') { $awardAttDmgPerKill.Add($p, [math]::Round($arrPlayerTable[$pos].Dmg / $arrPlayerTable[$pos].Kills) ) } + } + $count += 1 + } + + + #defence - Rnd2=T2 and Rnd2=T1 - Get Player list and get required Data sets + ## Generate Attack/Def Tables, e.g. for att Rnd1 = Team2 def + Rnd2 = Team1 def + $script:playerListDefRnd1 = ($arrTeamRnd1.Keys | foreach { if ($arrTeamRnd1.$_ -match '^(2|2&1)$' -and (arrClassTable-FindPlayerTotal $_ 1) -gt $round1EndTime - 60) { $_ } }) + $script:playerListDefRnd2 = ($arrTeamRnd2.Keys | foreach { if ($arrTeamRnd2.$_ -match '^(1|2&1)$' -and (arrClassTable-FindPlayerTotal $_ 2) -gt $arrResult.time - $round1EndTime - 60) { $_ } }) + + $count = 1 + foreach ($array in @($playerListDefRnd1, $playerListDefRnd2)) { + foreach ($p in $array) { + #disqualify a player if they were on multiple teams + if ($arrTeam.$p -notmatch '^(1|2)$') { continue } + $pos = arrFindPlayer -Table ([ref]$arrPlayerTable) -Player $p -Round $count + + if ($count -eq 1) { + $awardDefKills.Add($p, $arrPlayerTable[$pos].Kills) + $awardDefDeath.Add($p, $arrPlayerTable[$pos].Death ) + $awardDefDmg.Add( $p, $arrPlayerTable[$pos].Dmg ) + $awardDefDmgTaken.Add($p, $arrPlayerTable[$pos].DmgTaken) + $awardDefDmgTeam.Add( $p, $arrPlayerTable[$pos].DmgTeam) + $awardDefTkill.Add($p, $arrPlayerTable[$pos].TKill) + $awardDefKD.Add( $p, ($arrPlayerTable[$pos].Kills - $arrPlayerTable[$pos].Death) ) + } + else { + $awardDefKills.Add($p, (awardScaler $arrPlayerTable[$pos].Kills )) + $awardDefDeath.Add($p, (awardScaler $arrPlayerTable[$pos].Death)) + $awardDefDmg.Add( $p, (awardScaler $arrPlayerTable[$pos].Dmg)) + $awardDefDmgTaken.Add( $p, (awardScaler $arrPlayerTable[$pos].DmgTaken)) + $awardDefDmgTeam.Add( $p, (awardScaler $arrPlayerTable[$pos].DmgTeam)) + $awardDefTkill.Add($p, (awardScaler $arrPlayerTable[$pos].TKill)) + + $awardDefKD.Add( $p, (awardScaler ($arrPlayerTable[$pos].Kills - $arrPlayerTable[$pos].Death)) ) + } + if ($arrPlayerTable[$pos].Kills -notin $null, '', '0') { $awardDefDmgPerKill.Add($p, [math]::Round($arrPlayerTable[$pos].Dmg / $arrPlayerTable[$pos].Kills) ) } + + } + $count += 1 + } + + #function to tally up multiple sources + function awardTallyTables { + $htOut = @{} + $keyList = $args[0].Keys + $keyList += ($args[1].Keys -notmatch ($args[0].Keys -replace $regExReplaceFix)) + + foreach ($item in $keyList) { + $htOut.$item = $args[0].$item + $args[1].$item + } + return $htOut + } + + $awardAttDmgAll = awardTallyTables $awardAttDmg $awardAttDmgTaken + $awardDefDmgAll = awardTallyTables $awardDefDmg $awardDefDmgTaken + + # MR Magoo % higher than TK and TmDmg, then divide by two for an average. + $tkAvg = ($awardDefTKill.Values | Measure-Object -Average).Average + $tdAvg = ($awardDefDmgTeam.Values | Measure-Object -Average).Average + $awardDefMagoo = @{} + foreach ($p in $awardDefTKill.Keys) { $awardDefMagoo.$p += $awardDefTKill.$p / $tkAvg } + foreach ($p in $awardDefDmgTeam.Keys) { $awardDefMagoo.$p = [Math]::Round(($awardDefMagoo.$p + ($awardDefDmgTeam.$p / $tdAvg)) / 2, 2) } + Remove-Variable tkAvg, tdAvg + + + # Repeatable function for Killed Class Lookup + function awardFromKilledClass { + # p1 = att/def p2 = regex + $htOut = @{} + switch ($args[0]) { + 'Att' { $plRnd1 = $playerListAttRnd1; $plRnd2 = $playerListAttRnd2 } + 'Def' { $plRnd1 = $playerListDefRnd1; $plRnd2 = $playerListDefRnd2 } + Default { return } + } + + foreach ($item in $arrKilledClassRnd1.Keys -match $args[1]) { + $name = ($item -split '_')[0] + # Disqualify player if on multple teams + if ($arrTeam.$name -notmatch '^(1|2)$') { continue } + + if ($name -in $plRnd1) { $htOut.$name += $arrKilledClassRnd1.$item } + } + foreach ($item in $arrKilledClassRnd2.Keys -match $args[1]) { + $name = $($item -split '_')[0] + # Disqualify player if on multple teams + if ($arrTeam.$name -notmatch '^(1|2)$') { continue } + + if ($name -in $plRnd2) { $htOut.$name += (awardScaler $arrKilledClassRnd2.$item) } + } + + if ($htOut.Count -lt 1) { $htOut.'n/a' = 'n/a' } + return $htOut + } + + $awardAttKilledDemo = (awardFromKilledClass 'Att' '.*_4$') + $awardDefKilledSold = (awardFromKilledClass 'Def' '.*_3$') + $awardDefKilledLight = (awardFromKilledClass 'Def' '.*_(1|5|8|9)$') + $awardAttKilledHeavy = (awardFromKilledClass 'Att' '.*_(3|4|6|7)$') + $awardDefKilledHeavy = (awardFromKilledClass 'Def' '.*_(3|4|6|7)$') + $awardAttLightKills = (awardFromKilledClass 'Att' '.*_(1|5|8|9)_.*$') + $awardAttKilledSG = (awardFromKilledClass 'Att' '.*_10$') + + + ##### + #Get MAX and MIN for each of the new tables starting with awardAtt/awardDef + # CREATE ALL $awardDef and $awardAtt tables BEFORE THIS POINT!! + ###### + #attack + #### + foreach ($v in (Get-Variable 'award*' -Exclude '*_*', 'awardsHtml')) { + Set-Variable -Name "$($v.Name)_Max" -Value (($v.Value).Values | Measure-Object -Maximum).Maximum + Set-Variable -Name "$($v.Name)_Min" -Value (($v.Value).Values | Measure-Object -Minimum).Minimum + } + + function addTokenToString { + if ($args[0] -in $null, '') { $args[1] } + elseif ($args[1] -notin ($args[0] -split ', ')) { "$($args[0]), $($args[1])" } + else { $args[0] } + } + + # Get the Names of each Max/Min award table + foreach ($p in $PlayerList) { + foreach ($vlist in @((Get-Variable 'award*_Max' -Exclude '*Versus*'), (Get-Variable 'award*_Min' -Exclude '*Versus*'))) { + foreach ($v in $vlist) { + $arrayName = ($v.Name -Split '_')[0] + $name = "$($v.Name)Name" + $value = $v.Value[1] + if (Test-Path "variable:$($name)") { $leader = Get-Variable $name -ValueOnly } + else { $leader = '' } + + if ((Get-Variable $arrayName).Value.$p -eq $v.Value) { Set-Variable -Name $name -Value (addTokenToString $leader $p) } + } + } + } + + # Most Frag on a player + $attMax = '' + $awardAttPlayerFrag_MaxName = '' + $awardAttPlayerFrag_Victim = '' + $awardAttPlayerFrag_Value = '' + $defMax = '' + $awardDefPlayerFrag_MaxName = '' + $awardDefPlayerFrag_Victim = '' + $awardDefPlayerFrag_Value = '' + + $count = 1 + ### Frag versus statistics + foreach ($array in @($arrFragVersusRnd1, $arrFragVersusRnd2)) { + foreach ($item in $array.keys) { + #player/target + $pt = $item -split '_' + switch ($count) { + 1 { $pl = $playerListAttRnd1; $value = $array.$item } + default { $pl = $playerListAttRnd2; $value = awardScaler $array.$item } + } + + if ($pt[0] -ne $pt[1] -and $pt[0] -in $pl) { + if ($max -eq '' -or $value -ge $attMax) { + if ($value -eq $attMax) { + $awardAttPlayerFrag_MaxName = (addTokenToString $awardAttPlayerFrag_MaxName $pt[0]) + $awardAttPlayerFrag_Victim = (addTokenToString $awardAttPlayerFrag_Victim $pt[1]) + } + else { + $awardAttPlayerFrag_MaxName = $pt[0] + $awardAttPlayerFrag_Victim = $pt[1] + } + $awardAttPlayerFrag_Value = $value + $attMax = $value + } + } + + switch ($count) { + 1 { $pl = $playerListDefRnd1 } + default { $pl = $playerListDefRnd2 } + } + + if ($pt[0] -ne $pt[1] -and $pt[0] -in $pl) { + if ($max -eq '' -or $value -ge $defMax) { + if ($value -eq $defMax) { + $awardDefPlayerFrag_MaxName = (addTokenToString $awardDefPlayerFrag_MaxName $pt[0]) + $awardDefPlayerFrag_Victim = (addTokenToString $awardDefPlayerFrag_Victim $pt[1]) + } + else { + $awardDefPlayerFrag_MaxName = $pt[0] + $awardDefPlayerFrag_Victim = $pt[1] + } + $awardDefPlayerFrag_Value = $value + $defMax = $value + } + } + } + $count += 1 + } + + # Most Dmg on a player + $attMax = '' + $awardAttPlayerDmg_MaxName = '' + $awardAttPlayerDmg_Victim = '' + $awardAttPlayerDmg_Value = '' + $defMax = '' + $awardDefPlayerDmg_MaxName = '' + $awardDefPlayerDmg_Victim = '' + $awardDefPlayerDmg_Value = '' + + $count = 1 + ### Damage versus statistics + foreach ($array in @($arrDmgVersusRnd1, $arrDmgVersusRnd2)) { + foreach ($item in $array.keys) { + #player/target + $pt = $item -split '_' + switch ($count) { + 1 { $pl = $playerListAttRnd1; $value = $array.$item } + default { $pl = $playerListAttRnd2; $value = awardScaler $array.$item } + } + + if ($pt[0] -ne $pt[1] -and $pt[0] -in $pl) { + if ($max -eq '' -or $value -ge $attMax) { + if ($value -eq $attMax) { + $awardAttPlayerDmg_MaxName = (addTokenToString $awardAttPlayerDmg_MaxName $pt[0]) + $awardAttPlayerDmg_Victim = (addTokenToString $awardAttPlayerDmg_Victim $pt[1]) + } + else { + $awardAttPlayerDmg_MaxName = $pt[0] + $awardAttPlayerDmg_Victim = $pt[1] + } + $awardAttPlayerDmg_Value = $value + $attMax = $value + } + } + + switch ($count) { + 1 { $pl = $playerListDefRnd1 } + default { $pl = $playerListDefRnd2 } + } + + if ($pt[0] -ne $pt[1] -and $pt[0] -in $pl) { + if ($max -eq '' -or $value -ge $defMax) { + if ($value -eq $defMax) { + $awardDefPlayerDmg_MaxName = (addTokenToString $awardDefPlayerDmg_MaxName $pt[0]) + $awardDefPlayerDmg_Victim = (addTokenToString $awardDefPlayerDmg_Victim $pt[1]) + } + else { + $awardDefPlayerDmg_MaxName = $pt[0] + $awardDefPlayerDmg_Victim = $pt[1] + } + $awardDefPlayerDmg_Value = $value + $defMax = $value + } + + } + } + $count += 1 + } + + #### + # Make Award HTML String + ### + + function awardScaleCaveat { + # p1 = att/def #p2 = names + if ($arrResult.winningTeam -lt 2) { return $args[1] } + + $players = $args[1] -split ', ' + switch ($args[0]) { + 'Att' { $pl = $playerListAttRnd2 } + default { $pl = $playerListDefRnd2 } + } + + $outNames = '' + foreach ($p in $players) { + if ($p -in $pl) { $name = "$($p)*" } + else { $name = $p } + + if ($outNames -eq '') { $outNames = $name } + else { $outNames += ", $($name)" } + } + return $outNames + } + + + $awardsHtml = "
+

The Attackers

+ + + + + + + + + + + + + + + + + + " + + if ($arrResult.winningTeam -eq 2) { + $awardsHtml += "`n" + } + + $awardsHtml += "
Award Winner Description
Commando $(awardScaleCaveat 'Att' $awardAttKills_MaxName) Most kills ($($awardAttKills_Max))
Rambo $(awardScaleCaveat 'Att' $awardAttDmg_MaxName) Most damage ($($awardAttDmg_Max))
Golden Hands $($awardAttFlagCap_MaxName) Most caps ($($awardAttFlagCap_Max))
Running Man $($awardAttFlagTime_MaxName) Most time with flag ($($awardAttFlagTime_Max)s)
Brawler $(awardScaleCaveat 'Att' $awardAttKilledHeavy_MaxName)Most kills on heavy classes ($($awardAttKilledHeavy_Max))
David $(awardScaleCaveat 'Att' $awardAttLightKills_MaxName) Most kills as a light class ($($awardAttLightKills_Max))
Spec Ops $(awardScaleCaveat 'Att' $awardAttKilledDemo_MaxName) Most kills on demo ($($awardAttKilledDemo_Max))
Sapper $(awardScaleCaveat 'Att' $awardAttKilledSG_MaxName) Most kills on a SG ($($awardAttKilledSG_Max))
Lemming $(awardScaleCaveat 'Att' $awardAttDeath_MaxName) Most Deaths ($($awardAttDeath_Max))
Battering Ram $(awardScaleCaveat 'Att' $awardAttKD_MinName) Lowest Kill-Death rank ($($awardAttKD_Min))
Buck shot $($awardAttDmgPerKill_MaxName) Most Damage per kill ($($awardAttDmgPerKill_Max))
Predator $(awardScaleCaveat 'Att' $awardAttPlayerFrag_MaxName) Most kills on a defender ($($awardAttPlayerFrag_Value) on $($awardAttPlayerFrag_Victim))
Hulk Smash $(awardScaleCaveat 'Att' $awardAttPlayerDmg_MaxName) Most damage on a defender ($($awardAttPlayerDmg_Value) on $($awardAttPlayerDmg_Victim))
*Team2 scaled: Only $('{0:p0}' -f [math]::Round((1 - $arrResult.winRating),2)) of Rnd2 played
+
+

The Defenders

+ + + + + + + + + + + + + + + + + `n" + + if ($arrResult.winningTeam -eq 2) { + $awardsHtml += "`n" + } + $awardsHtml += "
Award Winner Description
Slaughterhouse $(awardScaleCaveat 'Def' $awardDefKills_MaxName) Most kills ($($awardDefKills_Max))
Terminator $(awardScaleCaveat 'Def' $awardDefKD_MaxName) Kills-death rank ($($awardDefKD_Max))
Juggernaut $(awardScaleCaveat 'Def' $awardDefDmg_MaxName) Most damage ($($awardDefDmg_Max))
Dark Knight $(awardScaleCaveat 'Def' $awardDefKilledSold_MaxName) Most kills on Soldier ($($awardDefKilledSold_Max))
Tank $(awardScaleCaveat 'Def' $awardDefKilledHeavy_MaxName)Most kills on a heavy class ($($awardDefKilledHeavy_Max))
Goliath $(awardScaleCaveat 'Def' $awardDefKilledLight_MaxName)Most kills on a light class ($($awardDefKilledLight_Max))
Sly Fox $(awardScaleCaveat 'Def' $awardDefDeath_MinName) Lowest Deaths ($($awardDefDeath_Min))
Team Player $($awardDefDmgPerKill_MaxName) Most damage per kill ($($awardDefDmgPerKill_Max))
Nemesis $(awardScaleCaveat 'Def' $awardDefPlayerFrag_MaxName) Most Kills on an attacker ($($awardDefPlayerFrag_Value) on $($awardDefPlayerFrag_Victim))
No quarter $($awardDefDmgPerKill_MinName) Lowest damage per kill ($($awardDefDmgPerKill_Min))
Attention whore $(awardScaleCaveat 'Def' $awardDefDmgAll_MaxName) Most damage given + taken ($($awardDefDmgAll_Max))
Shy Guy $(awardScaleCaveat 'Def' $awardDefDmgTaken_MinName) Lowest damage taken ($($awardDefDmgTaken_Min))
Mr Magoo $($awardDefMagoo_MaxName) Team Kill/Damage above avg ($('{0:p0}' -f $awardDefMagoo_Max))
*Team1 scaled: Only $('{0:p0}' -f [math]::Round((1 - $arrResult.winRating),2)) of Rnd2 played
" + + ### + # Generate the HTML Ouput + ### + Write-Host "$(Get-Date) | #3 HTML generation started" + $ccGrey = 'cellGrey' + $ccAmber = 'cellAmber' + $ccOrange = 'cellOrange' + $ccGreen = 'cellGreen' + $ccBlue = 'rowTeam1' + $ccRed = 'rowTeam2' + $ccPink = 'rowTeamBoth' + + <# See fo_stats.css + $ccGrey = '#F1F1F1' + $ccAmber = '#FFD900' + $ccOrange = '#FFB16C' + $ccGreen = '#96FF8F' + $ccBlue = '#87ECFF' + $ccRed = '#FF8080' + $ccPink = '#FA4CFF' + + #> + + $htmlOut = " + + + + + + + + + + + + + + +

$($jsonFile.Name)

" + + + $htmlOut += " + + `n
ResultScoresWin Rating
" + + switch ($arrResult.winningTeam) { + '0' { $htmlOut += "DRAW! " } + default { $htmlOut += "TEAM $($arrResult.winningTeam) WINS! " } + } + + $htmlOut += "Team1: $($arrResult.team1Score) vs Team2: $($arrResult.team2Score)$('{0:p0}' -f $arrResult.winRating) ($($arrResult.winRatingDesc))
`n" + + #### awards + $htmlOut += "

Awards

`n" + $htmlOut += $awardsHtml + + #Frag Total Table + #$htmlOut += "

TOTAL - Attack and Defence

`n" + #$htmlOut += GenerateFragHtmlTable '' + #$htmlOut += GenerateDmgHtmlTable '' + $htmlOut += "

Attack and Defence

`n" + $htmlOut += '
' + $htmlOut += "

Attack

`n" + $htmlOut += GenerateSummaryHtmlTable -Attack + $htmlOut += '
' + $htmlOut += "

Defence

`n" + $htmlOut += GenerateSummaryHtmlTable -Defence + $htmlOut += '
' + + $htmlOut += "

Frags

`n" + $htmlOut += '
' + $htmlOut += "

Round 1

`n" + $htmlOut += GenerateFragHtmlTable -Round '1' + $htmlOut += '
' + $htmlOut += "

Round 2

`n" + $htmlOut += GenerateFragHtmlTable -Round '2' + $htmlOut += '
' + + #Damage by Round Table + $htmlOut += "

Damage

`n" + $htmlOut += '
' + $htmlOut += "

Round 1

`n" + $htmlOut += GenerateDmgHtmlTable -Round '1' + $htmlOut += '
' + $htmlOut += "

Round 2

`n" + $htmlOut += GenerateDmgHtmlTable -Round '2' + $htmlOut += '
' + + ### + # frag/death per mins + ### + + $htmlOut += "

Per Minute - Frags/Deaths

`n" + + $table = '' + $tableHeader = "Rnd1Rnd2 + #PlayerTeamKillsDthTK" + + foreach ($min in 1..$timeBlock) { + $tableHeader += "$($min)" + if ($min -in $timeBlock, ([math]::Floor($round1EndTime / 60))) { + $tableHeader += "Total" + } + } + $tableHeader += "`n" + + $count = 1 + $subtotalFrg = @(1..$timeBlock | foreach { 0 } ) + $subtotalDth = @(1..$timeBlock | foreach { 0 } ) + + foreach ($p in $playerList) { + $kills = ($arrPlayerTable | Where Name -eq $p | Measure Kills -Sum).Sum + $death = ($arrPlayerTable | Where Name -eq $p | Measure Death -Sum).Sum + $tKill = ($arrPlayerTable | Where Name -eq $p | Measure TKill -Sum).Sum + $table += "$($count)$($p)$($arrTeam.$p)$($kills)$($death)$($tKill)" + + $count2 = 0 + foreach ($min in 1..$timeBlock) { + $key = "$($min)_$($p)" + $kills = $arrFragMin.$key + $dth = $arrDeathMin.$key + if ($kills -in '', $null -and $dth -in '', $null) { + $value = '' + $cellCC = nullValueColorCode + } + else { + $cellCC = $ccGreen + if ($kills -lt $dth) { $cellCC = $ccAmber } + if ($kills -eq '' -or $kills -lt 1) { $kills = '0'; $cellCC = $ccOrange } + if ($dth -eq '' -or $dth -lt 1) { $dth = '0' } + + $value = "$($kills)/$($dth)" + } + + $table += "$($value)" + + $subtotalFrg[$count2] += $kills + $subtotalDth[$count2] += $dth + + + if ($min -in $timeBlock, ([math]::Floor($round1EndTime / 60))) { + #rnd total + if ($min -le ([math]::Floor($round1EndTime / 60))) { + $round = 1 + } + else { + $round = 2 + } + $kills = ($arrPlayerTable | Where { $_.Name -eq $p -and $_.Round -eq $round } | Measure Kills -Sum).Sum + $death = ($arrPlayerTable | Where { $_.Name -eq $p -and $_.Round -eq $round } | Measure Death -Sum).Sum + $table += "$($kills)/$($death)" + } + $count2 += 1 + } + + $table += "`n" + $count += 1 + } + + $table += 'Total:' + $count = 0 + foreach ($st in $subtotalFrg) { + $table += "$([int]$subtotalFrg[$count])/$([int]$subtotaldth[$count])" + $count += 1 + if ($count -in $timeBlock, ([math]::Floor($round1EndTime / 60))) { $table += "" } + } + $table += "`n" + + $htmlOut += "" + $htmlOut += $tableHeader + $htmlOut += $table + $htmlOut += "
`n" + + ### + # Damage per mins + ### + $htmlOut += "

Per Minute - Damage (excluding friendly-fire)

`n" + + $table = '' + $subtotalDmg = @(1..$timeBlock | foreach { 0 } ) + $count = 1 + + $tableHeader = $tableHeader -replace '>Kills<', '>Dmg<' + + foreach ($p in $playerList) { + $dmg = ($arrPlayerTable | Where Name -eq $p | Measure Dmg -Sum).Sum + $death = ($arrPlayerTable | Where Name -eq $p | Measure Death -Sum).Sum + $tKill = ($arrPlayerTable | Where Name -eq $p | Measure TKill -Sum).Sum + $table += "$($count)$($p)$($arrTeam.$p)$($dmg)$($death)$($tKill)" + + $count2 = 0 + foreach ($min in 1..$timeBlock) { + $key = "$($min)_$($p)" + $dmg = $arrDmgMin.$key + if ($kills -eq '' -or $kills -lt 1) { $kills = 0 } + + $table += "$($dmg)" + + $subtotalDmg[$count2] += $dmg + + if ($min -in $timeBlock, ([math]::Floor($round1EndTime / 60))) { + #rnd total + if ($min -le ([math]::Floor($round1EndTime / 60))) { + $round = 1 + } + else { + $round = 2 + } + $dmg = ($arrPlayerTable | Where { $_.Name -eq $p -and $_.Round -eq $round } | Measure Dmg -Sum).Sum + $table += "$($dmg)" + } + + $count2 += 1 + } + + $table += "`n" + $count += 1 + } + + $table += 'Total:' + + $count = 0 + foreach ($st in $subtotalDmg) { + $table += "$($subtotalDmg[$count])" + $count += 1 + + if ($count -in $timeBlock, ([math]::Floor($round1EndTime / 60))) { $table += "" } + } + $table += "`n" + + $htmlOut += "" + $htmlOut += $tableHeader + $htmlOut += $table + $htmlOut += "
`n" + + + ### + # Flag Cap/Took/Drop per min + ### + $htmlOut += "

Per Minute - Flag stats

`n" + + $tableHeader = " + + " } + $tableHeader += "`n" + + $count = 1 + $subtotalCap = @(1..$timeBlock | foreach { 0 } ) + $subtotalTook = @(1..$timeBlock | foreach { 0 } ) + $subtotalThrow = @(1..$timeBlock | foreach { 0 } ) + + foreach ($p in $playerList) { + #$pos = arrFindPlayer -Table ([ref]$arrPlayerTable) -Player $p + $table += " + + + + + " + + $count2 = 0 + foreach ($min in 1..$timeBlock) { + $key = "$($min)_$($p)" + $cap = $arrFlagCapMin.$key + $took = $arrFlagTookMin.$key + $throw = $arrFlagThrowMin.$key + $stop = $arrFlagStopMin.$key + + if ($cap -in '', $null -and $took -in '', $null) { + if ($Stop -notin '', $null) { + $value = $stop + $cellCC = $ccAmber + } + else { + $value = '' + $cellCC = nullValueColorCode + } + } + else { + $subtotalCap[$count2] += $cap + if ($took -in '', $null) { $took = 0 } + $subtotalTook[$count2] += $Took + if ($throw -in '', $null) { $throw = 0 } + $subtotalThrow[$count2] += $Throw + $cellCC = $ccGreen + if ($cap -in '', $null) { $cap = '0'; $cellCC = $ccOrange } + $value = "$($cap)/$($Took)/$($throw)" + } + + $table += "" + $count2 += 1 + } + + $table += "`n" + $count += 1 + } + + $table += '' + $count = 0 + foreach ($st in $subtotalCap) { + $table += "" + $count += 1 + } + $table += "`n" + + $htmlOut += $tableHeader + $htmlOut += $table + $htmlOut += "
Rnd1 (Cp/Tk/Thr or Stop)Rnd2 (Cp/Tk/Thr or Stop)
#PlayerTeamCapsTookThrowTimeStop" + $table = '' + + foreach ($min in 1..$timeBlock) { $tableHeader += "$($min)
$($count)$($p)$($arrTeam.$p)$(($arrPlayerTable | Where {$_.Name -eq $p } | Measure FlagCap -Sum).Sum)$(($arrPlayerTable | Where {$_.Name -eq $p } | Measure FlagTake -Sum).Sum)$(($arrPlayerTable | Where {$_.Name -eq $p } | Measure FlagThrow -Sum).Sum)$("{0:m\:ss}" -f [timespan]::FromSeconds( ($arrPlayerTable | Where {$_.Name -eq $p } | Measure FlagTime -Sum).Sum ) )$(($arrPlayerTable | Where {$_.Name -eq $p } | Measure FlagStop -Sum).Sum)$($value)
Total:$($subtotalCap[$count])/$($subtotalTook[$count])/$($subtotalThrow[$count])
`n" + + Remove-Variable cellCC, value, took, cap, key, subtotalCap, subtotalTook + + ### + # Class related tables.... + ### + + $count = 1 + $tableHeader = " + + + + + + + + + + + + + + + + + + + + `n" + + $table = '' + $subtotalFrg = @($ClassAllowedwithSG | foreach { 0 }) + @($ClassAllowedwithSG | foreach { 0 }) + $subtotalDth = @($ClassAllowedwithSG | foreach { 0 }) + @($ClassAllowedwithSG | foreach { 0 }) + + foreach ($p in $playerList) { + $kills = ($arrPlayerTable | Where Name -eq $p | Measure Kills -sum).Sum + $table += "" + + $count2 = 0 + foreach ($round in 1..2) { + foreach ($o in ($ClassAllowedwithSG)) { + $kills = ($arrWeaponTable | Where { $_.Name -eq $p -and $_.Round -eq $round -and $_.Class -eq $o } | Measure-Object Kills -Sum).Sum + if ($o -eq '10') { + $dth = ($arrPlayerTable | Where { $_.Name -eq $p -and $_.Round -eq $round } | Measure-Object SGDeath -Sum).Sum + } else { + $dth = ($arrWeaponTable | Where { $_.Name -eq $p -and $_.Round -eq $round -and $_.PlayerClass -eq $o } | Measure-Object Death -Sum).Sum + } + + if ($kills + $dth -gt 0) { + $table += "" + } + else { + $table += "" + } + + $subtotalFrg[$count2] += $kills + $subtotalDth[$count2] += $dth + $count2 += 1 + } + } + + $table += "`n" + $count += 1 + } + + $table += '' + $count = 0 + foreach ($st in $subtotalFrg) { $table += ""; $count++ } + $htmlOut += '' + + #$htmlOut += '
' + #$htmlOut += '
' + $htmlOut += "

Kills/Deaths By Class

`n" + $htmlOut += $tableHeader + $htmlOut += $table + $htmlOut += '
Rnd1Rnd2
#PlayerTeamKillsScoSoldDemoMedHwGPyroSpyEngSGScoSoldDemoMedHwGPyroSpyEngSG
$($count)$($p)$($arrTeam.$p)$($kills)$($kills)/$($dth)
Total:$(if (0 -ne $subtotalFrg[$count] + $subtotalDth[$count]) { "$($subtotalFrg[$count])/$($subtotalDth[$count])" })
' + #$htmlOut += '
' + + + $table = '' + $tableHeader = " + + + " + + $count = 1 + foreach ($p in $playerList) { + $table += "" + + foreach ($r in 1..2) { + $pos = arrFindPlayer -Table ([ref]$arrPlayerTable) -Player $p -Round $r + if ($arrPlayerTable[$pos].Death -in 0, '', $null) { $kd = 'n/a' } + else { $kd = [math]::Round( $arrPlayerTable[$pos].Kills / $arrPlayerTable[$pos].Death , 2) } + + $table += "" + + $count2 = 1 + $pr = $arrClassTimeTable | Where-Object { $_.Name -eq $p -and $_.Round -eq $r } + + foreach ($o in $ClassAllowedStr) { + if ($pr.$o -lt 1) { $time = '' } + else { $time = "{0:m\:ss}" -f [timespan]::FromSeconds($pr.$o) } + + if ($time) { $table += "" } + else { $table += "" } + $count2 += 1 + } + } + $table += "`n" + $count += 1 + } + + + #'
' + $htmlOut += "

Estimated Time per Class

`n" + $htmlOut += $tableHeader + $htmlOut += $table + $htmlOut += '
Rnd1Rnd2
#PlayerTeamK/D + ScoSoldDemoMedHwGPyroSpyEngK/DScoSoldDemoMedHwGPyroSpyEng
$($count)$($p)$($arrTeam.$p)$($kd)$($time)
' + #$htmlOut += '
' + + #Stats for each player + $htmlOut += "

Player Weapon Stats

`n" + + $count = 1 + $table = '' + $tableHeader = "" + $tableHeader += "`n" + + $divCol = 1 + $htmlOut += '
' + $htmlOut += '
' + $htmlOut += '

Team 1

' + + foreach ($p in $playerList) { + if ($divCol -eq 1 -and $arrTeam.$p -gt 1) { + $htmlOut += '
' + $htmlOut += '

Team 2

' + $divCol += 1 + } + + $htmlOut += "

$($p)

" + + foreach ($rnd in 1..2) { + $pClassKeys = @() + $totalTime = 0 + $classStats = '' + $pClassKeys = getPlayerClasses $rnd $p + $pr = ($arrClassTimeTable | Where-Object { $_.Name -eq $p -and $_.Round -eq $rnd}) + $totalTime = arrClassTable-GetPlayerTotal $pr + if ($totalTime -gt 0) { + foreach ($i in ($pClassKeys -split ',')) { $classStats += "$i ($('{0:p0}' -f ($pr.$i / $totalTime))) " } + } + + $htmlOut += "Rnd$($rnd): $($classStats) $(if ($rnd -eq 1) {'|'}) " + } + Remove-Variable pClassKeys, totalTime, classStats + + $playerStats = '' + $lastClass = '' + $totKill = @(0, 0, 0) + $totDmg = @(0, 0, 0) + $totDth = @(0, 0, 0) + $totDmgTk = @(0, 0, 0) + $foundFF = 0 + + $allWeapKeys = $arrWeaponTable | Where { $_.Name -eq $p -and $_.Weapon -notmatch '^(world|suicide|.*-ff)$' } ` + | Select-Object Class, Weapon | Sort-Object Class, Weapon -Unique + $allWeapKeys += $arrWeaponTable | Where { $_.Name -eq $p -and $_.Weapon -match '^(world|suicide|.*-ff)$' } ` + | Select-Object Class, Weapon |Sort-Object Class, Weapon -Unique + + foreach ($w in $allWeapKeys) { + if ($w.Class -eq 10) { $class = 9 } + else { $class = $w.Class } + $weapon = $w.Weapon + + $objRnd1 = [PSCustomObject]@{ Name = $p; Class = $class; Kills = 0; Dmg = 0; Death = 0; DmgTaken = 0; AttackCount = 0; HitPercent = ''; pos = 1 } + $objRnd2 = [PSCustomObject]@{ Name = $p; Class = $class; Kills = 0; Dmg = 0; Death = 0; DmgTaken = 0; AttackCount = 0; HitPercent = ''; pos = 2 } + + foreach ($o in @($objRnd1, $objRnd2)) { + $item = ($arrWeaponTable | Where-Object { $_.Name -eq $p -and $_.Class -eq $w.class -and $_.Round -eq $o.pos -and $_.Weapon -eq $weapon }) + + $o.Kills = ($item.Kills | Measure-Object -Sum).Sum + $o.Dmg = ($item.Dmg | Measure-Object -Sum).Sum + $o.Death = ($item.Death | Measure-Object -Sum).Sum + $o.DmgTaken = ($item.DmgTaken | Measure-Object -Sum).Sum + $o.AttackCount = ($item.AttackCount | Measure-Object -Sum).Sum + + if ($o.AttackCount -gt 0) { + $o.HitPercent = ($item.DmgCount | Measure-Object -Sum).Sum / $o.AttackCount + } + } + + if ($class -ne $lastClass -and $foundFF -lt 1) { + if ($lastClass -ne '') { + $playerStats += "
+ + " + } + + if ($weapon -match '(world|suicide|-ff)$') { $foundFF++ } + + $lastClass = $class + $totKill = @(0, 0, 0) + $totDmg = @(0, 0, 0) + $totDth = @(0, 0, 0) + $totDmgTk = @(0, 0, 0) + } + + $totKill[0] += [double]$objRnd1.Kills; $totKill[1] += [double]$objRnd2.Kills; $totKill[2] = ([double]$totKill[0] + [double]$totKill[1]) + $totDmg[0] += [double]$objRnd1.Dmg; $totDmg[1] += [double]$objRnd2.Dmg; $totDmg[2] = ([double]$totDmg[0] + [double]$totDmg[1]) + $totDth[0] += [double]$objRnd1.Death; $totDth[1] += [double]$objRnd2.Death; $totDth[2] = ([double]$totDth[0] + [double]$totDth[1]) + $totDmgTk[0] += [double]$objRnd1.DmgTaken; $totDmgTk[1] += [double]$objRnd2.DmgTaken; $totDmgTk[2] = ([double]$totDmgTk[0] + [double]$totDmgTk[1]) + + if ($weapon -match '^(world|suicide|.*-ff$)') { + $ccNormOrSuicide = $ccAmber + } + else { + $ccNormOrSuicide = $ccOrange + } + + <# Removed Sub totals - too large + $subTotalShot = $objRnd1.AttackCount + $objRnd2.AttackCount + $subTotalHit = $objRnd1.HitPercent + $objRnd2.HitPercent + if ($subTotalHit -gt 0) { + $subTotalHit = $subTotalHit / ( ($objRnd1.AttackCount -gt 0 | %{ if ($_) { [int]1 } }) ` + + ($objRnd2.AttackCount -gt 0 | %{ if ($_) { [int]1 } }) ) + } else { $subTotalHit = '' } + + $subTotalKill = $objRnd1.Kills + $objRnd2.Kills + $subTotalDmg = $objRnd1.Dmg + $objRnd2.Dmg + $subTotalDth = $objRnd1.Death + $objRnd2.Death + $subTotalDmgTk = $objRnd1.DmgTaken + $objRnd2.DmgTaken#> + + $playerStats += " + + + + + + + + + + + + `n" + <# Removed Sub totals - too large + + + + + + `n"#> + + } #end foreach + + if ($foundFF) { $lastClass = 'Friendly' } + + $playerStats += " + + `n" + #`n" + + $htmlOut += $tableHeader + $htmlOut += $playerStats + $htmlOut += "
Rnd1Rnd2
WeaponClassShotsHit%KillsDmgDthDmgTShotsHit%KillsDmgDthDmgT
$($ClassToStr[$lastClass]) Totals:$(nullValueAsBlank $totKill[0])$(nullValueAsBlank $totDmg[0])$(nullValueAsBlank $totDth[0])$(nullValueAsBlank $totDmgTk[0])$(nullValueAsBlank $totKill[1])$(nullValueAsBlank $totDmg[1])$(nullValueAsBlank $totDth[1])$(nullValueAsBlank $totDmgTk[1])
$($weapon)$($ClassToStr[$class])$(nullValueAsBlank $objRnd1.AttackCount)$('{0:P0}' -f (nullValueAsBlank $objRnd1.HitPercent))$(nullValueAsBlank $objRnd1.Kills)$(nullValueAsBlank $objRnd1.Dmg)$(nullValueAsBlank $objRnd1.Death)$(nullValueAsBlank $objRnd1.DmgTaken)$(nullValueAsBlank $objRnd2.AttackCount)$('{0:P0}' -f (nullValueAsBlank $objRnd2.HitPercent))$(nullValueAsBlank $objRnd2.Kills)$(nullValueAsBlank $objRnd2.Dmg)$(nullValueAsBlank $objRnd2.Death)$(nullValueAsBlank $objRnd2.DmgTaken)
$(nullValueAsBlank $subTotalShot)$('{0:P0}' -f (nullValueAsBlank $subTotalHit))$(nullValueAsBlank $subTotalKill)$(nullValueAsBlank $subTotalDmg)$(nullValueAsBlank $subTotalDth)$(nullValueAsBlank $subTotalDmgTk)
$($lastClass) Totals:$(nullValueAsBlank $totKill[0])$(nullValueAsBlank $totDmg[0])$(nullValueAsBlank $totDth[0])$(nullValueAsBlank $totDmgTk[0])$(nullValueAsBlank $totKill[1])$(nullValueAsBlank $totDmg[1])$(nullValueAsBlank $totDth[1])$(nullValueAsBlank $totDmgTk[1])
$(nullValueAsBlank $totKill[2])$(nullValueAsBlank $totDmg[2])$(nullValueAsBlank $totDth[2])$(nullValueAsBlank $totDmgTk[2])
`n" + + $count += 1 + } + + $htmlOut += '' + $htmlOut += "" + $htmlOut += "" + + $htmlOut | Out-File -LiteralPath "$outFileStr.html" -Encoding utf8 + Write-Host "$(Get-Date) | #4 HTML generation ended" + if ($OpenHTML) { & "$outFileStr.html" } + } #end html generation + + + + ## Object/Table for Text-base Summaries + $arrResultTable += [pscustomobject]@{ + Match = $jsonFile.BaseName -replace '_blue_vs_red.*', '' + Winner = switch ($arrResult.winningTeam) { '0' { "Draw" }; "1" { "Team1" }; "2" { "Team2" } } + Rating = $arrResult.winRating + Score1 = $arrResult.team1Score + Score2 = $arrResult.team2Score + Team1 = ($playerlist | ForEach-Object { if ($arrTeam.$_ -match "1") { $_ } }) -join ',' + Team2 = ($playerlist | ForEach-Object { if ($arrTeam.$_ -match "2") { $_ } }) -join ',' + } + + foreach ($p in $playerList) { + #Data that is seperated by round - work out whos att and def. + foreach ($round in 1..2) { + $pos = arrFindPlayer ([ref]$arrPlayerTable) $p -Round $round + if ($pos -eq -1) { continue } # Did not play this round + + $aod = (attOrDef $round (Get-Variable "arrTeamRnd$round").Value.$p) + if ($aod -ne '') { + if ( $aod -eq 'Att') { $refSummary = ([ref]$arrSummaryAttTable); $refClassTime = ([ref]$arrClassTimeAttTable); $refClassFrag = ([ref]$arrClassFragAttTable) } + elseif ($aod -eq 'Def') { $refSummary = ([ref]$arrSummaryDefTable); $refClassTime = ([ref]$arrClassTimeDefTable); $refClassFrag = ([ref]$arrClassFragDefTable) } + + arrSummaryTable-UpdatePlayer -table $refSummary -player $p -kills ([int]$arrPlayerTable[$pos].Kills) ` + -death ([int]$arrPlayerTable[$pos].Death) ` + -tkill ([int]$arrPlayerTable[$pos].TKill) + arrSummaryTable-SetPlayerProperty -table $refSummary -player $p -property 'Dmg' -value ([double]$arrPlayerTable[$pos].Dmg) + + foreach ($i in $ClassAllowedWithSG) { + $time = [int]($arrClassTimeTable | Where-Object { $_.Name -eq $p -and $_.Round -eq $round}).($ClassToStr[$i]) + $kills = ($arrWeaponTable | Where-Object { $_.Name -eq $p -and $_.Class -eq $i -and $_.Round -eq $round } | Measure-Object Kills -Sum).Sum + $class = ($ClassToStr[$i]) + + if ($time -gt 0) { + arrClassTable-UpdatePlayer -table $refClassTime -player $p -class $class -value ([int]$time) + arrSummaryTable-SetPlayerProperty -table $refSummary -player $p -property 'TimePlayed' -value ([int]$time) + } + + if ($kills -gt 0) { + arrClassTable-UpdatePlayer -table $refClassFrag -player $p -class $class -value $kills + } + } + } + } + + #Data that are not divded up by round + switch ($arrResult.winningTeam) { + { $_ -eq '0' -or + $arrTeam.$p -match "[1-2]&[1-2]" } { + arrSummaryTable-SetPlayerProperty -table ([ref]$arrSummaryAttTable) -player $p -property 'Draw' + arrSummaryTable-SetPlayerProperty -table ([ref]$arrSummaryDefTable) -player $p -property 'Draw' + } + $arrPlayerTable[$pos].Team { + arrSummaryTable-SetPlayerProperty -table ([ref]$arrSummaryAttTable) -player $p -property 'Win' + arrSummaryTable-SetPlayerProperty -table ([ref]$arrSummaryDefTable) -player $p -property 'Win' + } + Default { + arrSummaryTable-SetPlayerProperty -table ([ref]$arrSummaryAttTable) -player $p -property 'Loss' + arrSummaryTable-SetPlayerProperty -table ([ref]$arrSummaryDefTable) -player $p -property 'Loss' + } + } + + arrSummaryTable-SetPlayerProperty -table ([ref]$arrSummaryAttTable) -player $p -property 'SGKills' -value (($arrPlayerTable | Where Name -EQ $p | Measure SGKills -Sum).Sum) + arrSummaryTable-SetPlayerProperty -table ([ref]$arrSummaryAttTable) -player $p -property 'FlagCap' -value (($arrPlayerTable | Where Name -EQ $p | Measure FlagCap -Sum).Sum) + arrSummaryTable-SetPlayerProperty -table ([ref]$arrSummaryAttTable) -player $p -property 'FlagTake' -value (($arrPlayerTable | Where Name -EQ $p | Measure FlagTake -Sum).Sum) + arrSummaryTable-SetPlayerProperty -table ([ref]$arrSummaryAttTable) -player $p -property 'FlagTime' -value (($arrPlayerTable | Where Name -EQ $p | Measure FlagTime -Sum).Sum) + arrSummaryTable-SetPlayerProperty -table ([ref]$arrSummaryDefTable) -player $p -property 'SGDeath' -value (($arrPlayerTable | Where Name -EQ $p | Measure SGDeath -Sum).Sum) + arrSummaryTable-SetPlayerProperty -table ([ref]$arrSummaryDefTable) -player $p -property 'FlagStop' -value (($arrPlayerTable | Where Name -EQ $p | Measure FlagStop -Sum).Sum) + } +} + + +#Value Per Minute +function Table-CalculateVPM { + param($Value, $TimePlayed, $Round) + + if ($Value -eq 0) { return $null } + if ($Round -in '', $null) { $Round = 2 } + + if (!$TimePlayed) { return '' } + return [math]::Round($Value / ($TimePlayed / 60), $Round) +} + +$textOut = '' +$textOut += "###############" +$textOut += "`n Game Log " +$textOut += "`n###############" +$textOut += $arrResultTable | Format-Table Match, Winner, @{L = 'Rating'; E = { '{0:P0}' -f $_.Rating } }, Score1, Team1, Score2, Team2 -Wrap | Out-String + +$textOut += "`n##############$(if ($jsonFileCount -gt 1) { "##############" })" +$textOut += "`n FINAL TOTALS $(if ($jsonFileCount -gt 1) { " - $jsonFileCount games" })" +$textOut += "`n##############$(if ($jsonFileCount -gt 1) { "##############" })`n" +$textOut += "`nAttack Summary`n" + +# Update the Attack Table into presentation format +foreach ($i in $arrSummaryAttTable) { + $i.KPM = Table-CalculateVPM $i.Kills $i.TimePlayed + if ($i.Death) { $i.KD = [math]::Round($i.Kills / $i.Death, 2) } + $i.DPM = Table-CalculateVPM $i.Dmg $i.TimePlayed 0 + $i.Classes = (Table-ClassInfo ([ref]$arrClassTimeAttTable) $i.Name $i.TimePlayed) + $i.FlagTime = "{0:m\:ss}" -f [timespan]::FromSeconds( $i.FlagTime ) + $i.TimePlayed = Format-MinSec $i.TimePlayed +} + +$textOut += $arrSummaryAttTable | Format-Table Name, KPM, KD, Kills, Death, TKill, @{L = 'Dmg'; E = { '{0:n0}' -f $_.Dmg } }, @{L = 'DPM'; E = { '{0:n0}' -f $_.DPM } }, @{L = 'SGKill'; E = { '{0:n0}' -f $_.SGKills } }, @{L = 'FlagCap'; E = { '{0:n0}' -f $_.FlagCap } }, @{L = 'FlagTake'; E = { '{0:n0}' -f $_.FlagTake } }, FlagTime, TimePlayed, Classes | Out-String + +# Update the Def Table into presentation format +foreach ($j in $arrSummaryDefTable) { + $j.KPM = Table-CalculateVPM $j.Kills $j.TimePlayed + $j.KD = [math]::Round($j.Kills / $j.Death, 2) + $j.DPM = Table-CalculateVPM $j.Dmg $j.TimePlayed 0 + $j.Classes = (Table-ClassInfo ([ref]$arrClassTimeDefTable) $j.Name $j.TimePlayed) + $j.TimePlayed = Format-MinSec $j.TimePlayed +} + +$textOut += "Defence Summary`n" +$textOut += $arrSummaryDefTable | Format-Table Name, KPM, KD, Kills, Death, TKill, @{L = 'Dmg'; E = { '{0:n0}' -f $_.Dmg } }, @{L = 'DPM'; E = { '{0:n0}' -f $_.DPM } }, @{L = 'SGDeath'; E = { '{0:n0}' -f $_.SGDeath } }, @{L = 'FlagStop'; E = { '{0:n0}' -f $_.FlagStop } }, Win, Draw, Loss, TimePlayed, Classes | Out-String + +$textOut += "Class Kills / KPM Summary - Attack`n" +$textOut += $arrClassFragAttTable | Format-Table Name, Sco, @{L = 'KPM'; E = { Table-CalculateVPM $_.Sco ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Sco } }, ` + Sold, @{L = 'KPM'; E = { Table-CalculateVPM $_.Sold ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Sold } }, ` + Demo, @{L = 'KPM'; E = { Table-CalculateVPM $_.Demo ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Demo } }, ` + Med, @{L = 'KPM'; E = { Table-CalculateVPM $_.Med ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Med } }, ` + HwG, @{L = 'KPM'; E = { Table-CalculateVPM $_.HwG ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).HwG } }, ` + Pyro, @{L = 'KPM'; E = { Table-CalculateVPM $_.Pyro ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Pyro } }, ` + Spy, @{L = 'KPM'; E = { Table-CalculateVPM $_.Spy ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Spy } }, ` + Eng, @{L = 'KPM'; E = { Table-CalculateVPM $_.Eng ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Eng } }, ` + SG, @{L = 'KPM'; E = { Table-CalculateVPM $_.SG ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Eng } } ` +| Out-String + +$textOut += "Class Kills / KPM Summary - Defence`n" +$textOut += $arrClassFragDefTable | Format-Table Name, Sco, @{L = 'KPM'; E = { Table-CalculateVPM $_.Sco ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Sco } }, ` + Sold, @{L = 'KPM'; E = { Table-CalculateVPM $_.Sold ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Sold } }, ` + Demo, @{L = 'KPM'; E = { Table-CalculateVPM $_.Demo ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Demo } }, ` + Med, @{L = 'KPM'; E = { Table-CalculateVPM $_.Med ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Med } }, ` + HwG, @{L = 'KPM'; E = { Table-CalculateVPM $_.HwG ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).HwG } }, ` + Pyro, @{L = 'KPM'; E = { Table-CalculateVPM $_.Pyro ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Pyro } }, ` + Spy, @{L = 'KPM'; E = { Table-CalculateVPM $_.Spy ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Spy } }, ` + Eng, @{L = 'KPM'; E = { Table-CalculateVPM $_.Eng ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Eng } }, ` + SG, @{L = 'KPM'; E = { Table-CalculateVPM $_.SG ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Eng } } ` +| Out-String + + +<# Moved class time to % in summary table. +$textOut += "`nClass Time Played - Attack`n" +$textOut += $arrClassTimeAttTable | Format-Table Name, ` + @{L='Sco' ; E={Format-MinSec $_.Sco}}, ` + @{L='Sold'; E={Format-MinSec $_.Sold}}, ` + @{L='Demo'; E={Format-MinSec $_.Demo}}, ` + @{L='Med' ; E={Format-MinSec $_.Med}}, ` + @{L='HwG' ; E={Format-MinSec $_.HwG}}, ` + @{L='Pyro'; E={Format-MinSec $_.Pyro}}, ` + @{L='Spy' ; E={Format-MinSec $_.Spy}}, ` + @{L='Eng' ; E={Format-MinSec $_.Eng}} ` + | Out-String +$textOut += "`nClass Time Played - Defence`n" +$textOut += $arrClassTimeDefTable | Format-Table Name, ` + @{L='Sco' ; E={Format-MinSec $_.Sco}}, ` + @{L='Sold'; E={Format-MinSec $_.Sold}}, ` + @{L='Demo'; E={Format-MinSec $_.Demo}}, ` + @{L='Med' ; E={Format-MinSec $_.Med}}, ` + @{L='HwG' ; E={Format-MinSec $_.HwG}}, ` + @{L='Pyro'; E={Format-MinSec $_.Pyro}}, ` + @{L='Spy' ; E={Format-MinSec $_.Spy}}, ` + @{L='Eng' ; E={Format-MinSec $_.Eng}} ` + | Out-String +#> + +if (!$NoStatJson) { + $textJsonOut = ([PSCustomObject]@{Matches = ''; SummaryAttack = ''; SummaryDefence = ''; ClassFragAttack = ''; ClassFragDefence = ''; ClassTimeAttack = ''; ClassTimeDefence = '' }) + $textJsonOut.Matches = @($arrResultTable | Select-Object Match, Winner, @{L = 'Rating'; E = { '{0:P0}' -f $_.Rating } }, Score1, Team1, Score2, Team2) + + $textJsonOut.SummaryAttack = [array]($arrSummaryAttTable) # | Select-Object -Property Name,KPM,KD,Kills,Death,TKill,Dmg,DPM,FlagStop,Win,Draw,Loss,TimePlayed,Classes) + $textJsonOut.SummaryDefence = [array]($arrSummaryDefTable) # | Select-Object -Property Name,KPM,KD,Kills,Death,TKill,Dmg,DPM,FlagStop,Win,Draw,Loss,TimePlayed,Classes) + + $textJsonOut.ClassFragAttack = [array]($arrClassFragAttTable | Select-Object -Property Name, ` + Sco, @{L = 'KPM1'; E = { Table-CalculateVPM $_.Sco ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Sco } }, ` + Sold, @{L = 'KPM3'; E = { Table-CalculateVPM $_.Sold ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Sold } }, ` + Demo, @{L = 'KPM4'; E = { Table-CalculateVPM $_.Demo ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Demo } }, ` + Med, @{L = 'KPM5'; E = { Table-CalculateVPM $_.Med ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Med } }, ` + HwG, @{L = 'KPM6'; E = { Table-CalculateVPM $_.HwG ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).HwG } }, ` + Pyro, @{L = 'KPM7'; E = { Table-CalculateVPM $_.Pyro ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Pyro } }, ` + Spy, @{L = 'KPM8'; E = { Table-CalculateVPM $_.Spy ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Spy } }, ` + Eng, @{L = 'KPM9'; E = { Table-CalculateVPM $_.Eng ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Eng } }, ` + SG, @{L = 'KPM0'; E = { Table-CalculateVPM $_.SG ($arrClassTimeAttTable | Where-Object Name -EQ $_.Name).Eng } }) + $textJsonOut.ClassFragDefence = ($arrClassFragDefTable | Select-Object -Property Name, ` + Sco, @{L = 'KPM1'; E = { Table-CalculateVPM $_.Sco ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Sco } }, ` + Sold, @{L = 'KPM3'; E = { Table-CalculateVPM $_.Sold ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Sold } }, ` + Demo, @{L = 'KPM4'; E = { Table-CalculateVPM $_.Demo ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Demo } }, ` + Med, @{L = 'KPM5'; E = { Table-CalculateVPM $_.Med ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Med } }, ` + HwG, @{L = 'KPM6'; E = { Table-CalculateVPM $_.HwG ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).HwG } }, ` + Pyro, @{L = 'KPM7'; E = { Table-CalculateVPM $_.Pyro ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Pyro } }, ` + Spy, @{L = 'KPM8'; E = { Table-CalculateVPM $_.Spy ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Spy } }, ` + Eng, @{L = 'KPM9'; E = { Table-CalculateVPM $_.Eng ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Eng } }, ` + SG, @{L = 'KPM0'; E = { Table-CalculateVPM $_.SG ($arrClassTimeDefTable | Where-Object Name -EQ $_.Name).Eng } }) + + $textJsonOut.ClassTimeAttack = [array]($arrClassTimeAttTable | Select-Object -Property Name, Sco, Sold, Demo, Med, HwG, Pyro, Spy, Eng) + $textJsonOut.ClassTimeDefence = [array]($arrClassTimeDefTable | Select-Object -Property Name, Sco, Sold, Demo, Med, HwG, Pyro, Spy, Eng) + + if ($jsonFileCount -eq 1) { + $TextFileStr = "$($outFileStr)_stats.json" + } else { + $TextFileStr = "$($inputfile[0].Directory.FullName)\FO_Stats_Summary-$($jsonFileCount)games-$('{0:yyMMdd_HHmmss}' -f (Get-Date)).json" + } + + write-host $outStr + ($textJsonOut | ConvertTo-Json) | Out-File -LiteralPath "$($TextFileStr)" -Encoding utf8 + Write-Host "JSON stats saved: $($outFileStr)_stats.json" +} + +Write-Host "`n" +Write-Host $textOut + +if ($TextSave) { + if ($jsonFileCount -eq 1) { + $TextFileStr = "$outFileStr.txt" + } else { + $TextFileStr = "$($inputfile[0].Directory.FullName)\FO_Stats_Summary-$($jsonFileCount)games-$('{0:yyMMdd_HHmmss}' -f (Get-Date)).txt" + } + $textOut | Out-File -LiteralPath $TextFileStr -Encoding utf8 + Write-Host "Text stats saved: $TextFileStr" +} + + +<# test Weap counter +$arrWeaponTable | ` # | Where { ($_.AttackCount + $_.DmgCount) -GT 0 } ` + Sort-Object Round,Team,Name,Class,Weapon ` + | FT *, ` #Name, ` + #Team, ` + #Round, ` + #Weapon, ` + #Dmg, ` + @{L='Shots';E={$_.AttackCount}}, ` + @{L='Hit%';E={ '{0:P0}' -f ($_.DmgCount / $_.AttackCount) }}, ` + @{L='DmgPerAtt';E={ '{0,5:n1}' -f ($_.Dmg / $_.AttackCount) }}, ` + @{L='DmgPerHit';E={ '{0,5:n1}' -f ($_.Dmg / $_.DmgCount) }} -GroupBy Round + #> +<# Testing New arrClassTimeTable.... the Att/Def is more accurate for some reason + $arrClassTimetable | FT *,@{L='Total';E={ $_.Sco + $_.Sold + $_.Demo + $_.Hwg + $_.Med + $_.Pyro + $_.Spy + $_.Eng }} + # These use the original hash table.... + $arrClassTimeAttTable | FT *,@{L='Total';E={ $_.Sco + $_.Sold + $_.Demo + $_.Hwg + $_.Med + $_.Pyro + $_.Spy + $_.Eng }} + $arrClassTimeDefTable | FT *,@{L='Total';E={ $_.Sco + $_.Sold + $_.Demo + $_.Hwg + $_.Med + $_.Pyro + $_.Spy + $_.Eng }} + #> + + +# SIG # Begin signature block +# MIIboAYJKoZIhvcNAQcCoIIbkTCCG40CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB +# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR +# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUd41kx8KdGdj/+/esnLATjrFg +# ztagghYVMIIDCDCCAfCgAwIBAgIQVxZN0cTEa7NFKtjIhSbFETANBgkqhkiG9w0B +# AQsFADAcMRowGAYDVQQDDBFIYXplIEF1dGhlbnRpY29kZTAeFw0yMzAyMTAwNTM3 +# MzRaFw0yNDAyMTAwNTU3MzRaMBwxGjAYBgNVBAMMEUhhemUgQXV0aGVudGljb2Rl +# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1M46e4LcpTytNDpPe4AN +# aUJrafdLCl23kH5G7qTsBRRwI6qpRhZc5TBI19oEwulC4t8h7nI6D78kYx8FdI2h +# tb0wWmhcPBAAT7iywe0G0Q3NgZxZKOyI0yto69Z7TMPnbGKhCegPuvAT0LejgTrK +# +OAH0a/uGBVCGgu1EsIOtVitWsuxTKNR5bX3b2Zoc1xaEVOMFGy74IvXzIx+VyaN +# pSH6JYo3iSLWmQNRMBvMPsRfcvkh9R1DXemAJX3LHEm0Bei3xco+20zhRQtCO1Md +# rAEAL3aq3oFDQ2KV1RCNqo5kvnwBBDumAveg7JnjuUd3DvqCF0+Hs+q2KjRy02vt +# MQIDAQABo0YwRDAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMw +# HQYDVR0OBBYEFFwWkpE7S3mEJiqQ8mTrbI0pUNm+MA0GCSqGSIb3DQEBCwUAA4IB +# AQC9XvfYZgwF/9CysiCloHccqkH8T+AJJCX1Uq1lZ9DTO7olZfTmrI/JQWrSDdrK +# hsEi9wp6uTjfapE18EQW9CBIAMNLFjhFv/uLGEp9vZpUWpuG+2hqVu0thtZbw/Gm +# gMh8yhm/lTXUj1tcltPDkgWnd2u44O2fdF2kE6hevBEXM71a45OiMic3SgpLi6m3 +# nMrsdQ0wu3qDm5I2Tm/Htq7Telmq0V3Lu1CKK6lYxxR1+Epsxumyiu0q9IKeKLMR +# RlbKKqHPcKoOadWNfZ1kR+5sO4q3+Tnc1evHS4HbJHQVZtR2k/tdsOvNi8qk9Sh9 +# +GFTkJDGhZR9TsyW2Se2KTUMMIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAY +# WjANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl +# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdp +# Q2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5 +# MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkw +# FwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVz +# dGVkIFJvb3QgRzQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBz +# aN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbr +# VsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTR +# EEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJ +# z82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyO +# j4DatpGYQJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6R +# AXwhTNS8rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k +# 98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJ +# tppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUa +# dmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZB +# dd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVf +# nSD8oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0T +# AQH/BAUwAwEB/zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0j +# BBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsG +# AQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29t +# MEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNl +# cnRBc3N1cmVkSURSb290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9j +# cmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYD +# VR0gBAowCDAGBgRVHSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3Qb +# PbYW1/e/Vwe9mqyhhyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5 +# +KH38nLeJLxSA8hO0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+n +# BgMTdydE1Od/6Fmo8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc +# /RzY9HdaXFSMb++hUD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVr +# zyerbHbObyMt9H5xaiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o +# 4rmUMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipeWzANBgkqhkiG9w0BAQsF +# ADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +# ExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJv +# b3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1OTU5WjBjMQswCQYDVQQG +# EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0 +# IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMIICIjAN +# BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1BkmzwT1ySVFVxyUDxPKRN6mX +# UaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkLf50fng8zH1ATCyZzlm34 +# V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C3GeO6lE98NZW1OcoLevT +# sbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5n12sy+iEZLRS8nZH92GD +# Gd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUdzTYNXNXmG6jBZHRAp8By +# xbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWHpo9OdhVVJnCYJn+gGkcg +# Q+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/oN7jPqJz+ucfWmyU8lKV +# EStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPVA+C/8KI8ykLcGEh/FDTP +# 0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg0ixXNXkrqPNFYLwjjVj3 +# 3GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mMDDtbiiKowSYI+RQQEgN9 +# XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6EVO7O6V3IXjASvUaetdN2 +# udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYD +# VR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1UdIwQYMBaAFOzX44LScV1k +# TN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcD +# CDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj +# ZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t +# L0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0 +# cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmww +# IAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUA +# A4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBTzr8Y+8dQXeJLKftwig2q +# KWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/EUExiHQwIgqgWvalWzxVz +# jQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fmniye4Iqs5f2MvGQmh2yS +# vZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szwcqMj+sAngkSumScbqyQe +# JsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8THwcFqcdnGE4AJxLafzYeH +# JLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/JWOZJyw9P2un8WbDQc1P +# tkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9Pzt4rUyt+8SVe+0KXzM5 +# h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm228Vex4Ziza4k9Tm8heZ +# Wcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVBtzrVFZgxtGIJDwq9gdkT +# /r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnwZXZCpimHCUcr5n8apIUP +# /JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv27dZ8/DCCBsIwggSqoAMC +# AQICEAVEr/OUnQg5pr/bP1/lYRYwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMC +# VVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBU +# cnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yMzA3 +# MTQwMDAwMDBaFw0zNDEwMTMyMzU5NTlaMEgxCzAJBgNVBAYTAlVTMRcwFQYDVQQK +# Ew5EaWdpQ2VydCwgSW5jLjEgMB4GA1UEAxMXRGlnaUNlcnQgVGltZXN0YW1wIDIw +# MjMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjU0WHHYOOW6w+VLMj +# 4M+f1+XS512hDgncL0ijl3o7Kpxn3GIVWMGpkxGnzaqyat0QKYoeYmNp01icNXG/ +# OpfrlFCPHCDqx5o7L5Zm42nnaf5bw9YrIBzBl5S0pVCB8s/LB6YwaMqDQtr8fwkk +# lKSCGtpqutg7yl3eGRiF+0XqDWFsnf5xXsQGmjzwxS55DxtmUuPI1j5f2kPThPXQ +# x/ZILV5FdZZ1/t0QoRuDwbjmUpW1R9d4KTlr4HhZl+NEK0rVlc7vCBfqgmRN/yPj +# yobutKQhZHDr1eWg2mOzLukF7qr2JPUdvJscsrdf3/Dudn0xmWVHVZ1KJC+sK5e+ +# n+T9e3M+Mu5SNPvUu+vUoCw0m+PebmQZBzcBkQ8ctVHNqkxmg4hoYru8QRt4GW3k +# 2Q/gWEH72LEs4VGvtK0VBhTqYggT02kefGRNnQ/fztFejKqrUBXJs8q818Q7aESj +# pTtC/XN97t0K/3k0EH6mXApYTAA+hWl1x4Nk1nXNjxJ2VqUk+tfEayG66B80mC86 +# 6msBsPf7Kobse1I4qZgJoXGybHGvPrhvltXhEBP+YUcKjP7wtsfVx95sJPC/QoLK +# oHE9nJKTBLRpcCcNT7e1NtHJXwikcKPsCvERLmTgyyIryvEoEyFJUX4GZtM7vvrr +# kTjYUQfKlLfiUKHzOtOKg8tAewIDAQABo4IBizCCAYcwDgYDVR0PAQH/BAQDAgeA +# MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkw +# FzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaa +# L3WMaiCPnshvMB0GA1UdDgQWBBSltu8T5+/N0GSh1VapZTGj3tXjSTBaBgNVHR8E +# UzBRME+gTaBLhklodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz +# dGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcB +# AQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFgG +# CCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRU +# cnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3 +# DQEBCwUAA4ICAQCBGtbeoKm1mBe8cI1PijxonNgl/8ss5M3qXSKS7IwiAqm4z4Co +# 2efjxe0mgopxLxjdTrbebNfhYJwr7e09SI64a7p8Xb3CYTdoSXej65CqEtcnhfOO +# HpLawkA4n13IoC4leCWdKgV6hCmYtld5j9smViuw86e9NwzYmHZPVrlSwradOKmB +# 521BXIxp0bkrxMZ7z5z6eOKTGnaiaXXTUOREEr4gDZ6pRND45Ul3CFohxbTPmJUa +# VLq5vMFpGbrPFvKDNzRusEEm3d5al08zjdSNd311RaGlWCZqA0Xe2VC1UIyvVr1M +# xeFGxSjTredDAHDezJieGYkD6tSRN+9NUvPJYCHEVkft2hFLjDLDiOZY4rbbPvlf +# sELWj+MXkdGqwFXjhr+sJyxB0JozSqg21Llyln6XeThIX8rC3D0y33XWNmdaifj2 +# p8flTzU8AL2+nCpseQHc2kTmOt44OwdeOVj0fHMxVaCAEcsUDH6uvP6k63llqmjW +# Iso765qCNVcoFstp8jKastLYOrixRoZruhf9xHdsFWyuq69zOuhJRrfVf8y2OMDY +# 7Bz1tqG4QyzfTkx9HmhwwHcK1ALgXGC7KP845VJa1qwXIiNO9OzTF/tQa/8Hdx9x +# l0RBybhG02wyfFgvZ0dl5Rtztpn5aywGRu9BHvDwX+Db2a2QgESvgBBBijGCBPUw +# ggTxAgEBMDAwHDEaMBgGA1UEAwwRSGF6ZSBBdXRoZW50aWNvZGUCEFcWTdHExGuz +# RSrYyIUmxREwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAw +# GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisG +# AQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFGBXE2us6VxlQPA4ljt2Ztm9saaIMA0G +# CSqGSIb3DQEBAQUABIIBAJd1DzmNudILZUUznvvWll8pzfR4El8ZXMOGStR0y7Kr +# N9O49qQX/35NMAKdBYKEEE3uB48qzxea9fyivQNQkwap3RAhK/135biT5fZ7FEuQ +# XkHMTlK7BN4HQuIgCJOCfKe0zobMLCdK3nDaGYhOEUq4f1YkzHMmjO2l5iNfwR0h +# JYsxODjLJZdplDtJopScb0B8nMD8zDFqyyacpMG9R/zKLrVGw3Wz9EMzoaAA+1gA +# 3VFJi7UHkgnjHh1WVB9O853tecoR2IVU+9fdirNxAHaHpkt4pXQloOL5xvF+xIgt +# egbHC07o7mmKF7jd9+ktS0VpSxIao+F0d6ZdnGpA1EihggMgMIIDHAYJKoZIhvcN +# AQkGMYIDDTCCAwkCAQEwdzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl +# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT +# SEEyNTYgVGltZVN0YW1waW5nIENBAhAFRK/zlJ0IOaa/2z9f5WEWMA0GCWCGSAFl +# AwQCAQUAoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUx +# DxcNMjMwODIyMDUyODI3WjAvBgkqhkiG9w0BCQQxIgQgc8QgQ+g3uiUzdPxYCw6N +# tPXdEX6wIcVDZiyfoAZ+42cwDQYJKoZIhvcNAQEBBQAEggIASyd65f0qRD91pcgl +# u+HuGtF+HybkAlNu85LD+1WCbLbl9GJYNE+SCZFsgkZFnzziujrUUdKwq64wEk/6 +# c92SLDVUCoAekX6CySdfXzwQWpd5Io8xLcz5It5MaBLg8lHtuyUKS2AHYIeWWRxY +# Yp7mVqXKrnNgsjcx2m8pXKHiqFN05zuhDsWHo15h1Zt/PfLtBP15aoLb+GoXMfK/ +# 4i56HDv2puj8G2YmatxC5rQ9xRGg/PbQi1/NN9LncZWJtR0R7Gjcp5SwlkQoOELf +# Cy/sP3tv+zuEIeFr1GI3ciEAT34V48+lx8H7Vk+3qCbZd41jV8oTPlPCkgBwKgHN +# RPUjBzii/gkZap/mwzA3vHVYwYo+v3fISYFB5gh5QEysI3rsJmnttJgCTd4DIsa8 +# fivqalICecW1ORuFChwMH7rCC3qV34X611VUbS/3FVYyjZVgi3LCmX/F63Yj/q40 +# Fw7J+JFbBNQG0S/bXdsKW0kAfoywVYWI/KdiTSc+Juz5KnazuWBwhZw0JSSJzABk +# Yao0xE0xBI5iHPfERYXwfxlA3ea0ifLfYeaNfLq1EgVUUdCI1ntyZAwGNK+I+XqD +# FU9gpWhDzS9Of1OyqJufBMBM0r6pdpn51sw357h10DcThambZWJPNlgyChNU/iBf +# aCeftF25gCKJBT9cbi/G+MiCBnM= +# SIG # End signature block diff --git a/README.md b/README.md index 09acb01..e49f6d0 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,11 @@ This takes the json stats object and converts it to a html file. Made by haze. ``` -PS COMMAND LINE:- & .\script.ps1 'x:\path\filename.json' [] -WIN COMMAND LINE:- powershell -Command "& .\script.ps1" "x:\path\filename.json" [] -PS *.JSON:- foreach ($f in (gci 'H:\stats\*.json')) { & .\FO_stats_v1.ps1 ($f.ToString() -replace '\[','`[' -replace '\]','`]') } +PS COMMAND LINE:- & .\FO_stats_v2.ps1 -StatFile 'x:\path\filename.json' [-RoundTime ] [-TextOnly] [-TextSave] [-NoStatJson] +WIN COMMAND LINE:- powershell -Command "& .\FO_stats_v2.ps1 -StatFile 'x:\path\filename.json' [-RountTime ] [-TextOnly] [-TextSave] [-NoStatJson]" + +NOTE: StatFile parameter now accepts *.json wildcard to generate many HTMLs, Text/Json stats are ALL STATS COMBINED. + +For individual TextJson Only stats for many stat files - i.e. not all games combined. +PS *.JSON:- foreach ($f in (gci 'x:\stats\*.json')) { & .\FO_stats_v2.ps1 -StatFile ($f.ToString() -replace '\[','`[' -replace '\]','`]') -TextOnly } ``` diff --git a/_FoDownloader.ps1 b/_FoDownloader.ps1 new file mode 100644 index 0000000..6432ae7 --- /dev/null +++ b/_FoDownloader.ps1 @@ -0,0 +1,565 @@ +###################################################### +# FO Stats Downloader from the FortressOne AWS Bucket +###################################################### +# Sample Commands +#----------------------------------------------------- +# 1. US Stats from last 24hrs. +# & .\_FoDownloader.ps1 -Region US +# +# 2. Euro demos from last 7 days: +# & .\_FoDownloader.ps1 -Region EU -LimitDays 7 -Demos +# +# 3. Stats from 1x server in last 24hrs. +# & .\_FoDownloader.ps1 -FilterPath 'sydney/staging/' +# +# 4. Last stat file availabe in ALL regions. +# & .\_FoDownloader.ps1 -Region ALL -LatestFile +# +# 5. Re-download existing stats from last 2hrs. +# & .\_FoDownloader.ps1 -Region US -LimitMins 120 -Overwrite +# +# 6. Do not download stats again but run FO Stats again. +# & .\_FoDownloader.ps1 -Region US -ForceStats +# +# 7. Demos from ALL regions - Go servers only in last 24hrs. +# & .\_FoDownloader.ps1 -Region ALL -FilterPath 'staging/' -Demos +# +# 8. Results from in Sydney & Dallas Stagin where map = well6. +# & .\_FoDownloader.ps1 -FilterPath 'sydney/staging/,dallas/statging/' -FilterFile '*`[well6`]*' +# +# 9. Stats from ALL regions, save to specific folder and save text-based output. +# & .\_FoDownloader.ps1 -Region ALL -OutFolder 'C:\FoStats' -TextSave +# +###################################################### + + +param ( + #AWS Result limit is 1000 files, so please filter to target your results + [switch]$Demos, #Demos instead of stats + [ValidateSet('ALL','US','BR','EU','OCE','INT')] + $Region, # All | US | BR | EU | OCE | Int + [switch]$AwsCLI, # Requires AWS CLI, scans all paths on the AWS bucket (stagine|quad|fo|hue), ignores region/filterpath + [string]$LocalFile, # + [string]$FilterPath, #Stats folder on repo, replicated locally, default='sydney/staging/' + [string]$FilterFile, #Filter the filenames from the XML results, use * for wildcards. + [string]$OutFolder, #Path of ouput JSON and HTML + [switch]$LatestFile, #Last modified file only (from the filtered list) + $TargetDate, #Date pointer - limited by -LimitDate or -LimitDays/-LimitMins + $LimitDate, + [int] $LimitMins, #Only access files from last X minutes (sum of days and mins)(sum of days and mins) + [double]$LimitDays, #Only access files from last X days (sum of days and mins) + [switch]$DownloadOnly, #Download JSON only + [switch]$Overwrite, #Force re-download do the FO_stats again even when file exists + [switch]$ForceStats, #Force running stats on already existing file + [switch]$DailyBatch, #For HTTP server daily tallying functions (no use on client) + [switch]$MonthlyBatch, #For HTTP server monthly tallying functions (no use on client) + [switch]$FullBatch, #Remove and regenerate above stats + [switch]$NewOnlyBatch, #Add newly process files to all batches + [switch]$PeriodBatch, #For HTTP server last 24hr / 7days stats + [switch]$PeriodExpire, + ### FO_Stats parameters ################### + [int] $RoundTime, #Passed to FO_Stats + [switch]$TextSave, #Passed to FO_Stats + [switch]$NoStatJson, #Passed to FO_stats + [switch]$TextOnly, #Passed to FO_Stats + [switch]$OpenHTML, #Passed to FO_Stats + [switch]$CleanUp #Delete JSON after stats. +) + +if ($Demos) { $AwsUrl = 'https://fortressone-demos.s3.amazonaws.com/' } +else { $AwsUrl = 'https://fortressone-stats.s3.amazonaws.com/' } + +if ($FullBatch -and !$DailyBatch -and !$MonthlyBatch) { $DailyBatch = $true; $MonthlyBatch = $true} +if ($NewOnlyBatch) { $DailyBatch = $true; $MonthlyBatch = $true } +if ($PeriodBatch -and $PeriodExpire) { $PeriodExpire = $false } +if ($FullBatch -or $NewOnlyBatch -or $DailyBatch -or $MonthlyBatch -or $PeriodBatch -or $PeriodExpire) { $CleanUp = $true } + +function GetPathFromFileName { + param( $fileName ) + + $arrPath = $fileName -split '[\\/]' + + if ($fileName -match 'brazil[\\/]fo[\\/]upload[\\/]') { + return $arrPath[-4] + '/' + $arrPath[-3] + '/' + $arrPath[-2] + '/' + } else { + return $arrPath[-3] + '/' + $arrPath[-2] + '/' + } +} + +# Update me for -Region parameter and Daily Stats updates +$OCEPaths = @('sydney/','melbourne/') +$USPaths = @('california/','dallas/','virginia/','miami/','phoenix/','atlanta/') +$BRPaths = @('saopaulo/','fortaleza/') +$EUPaths = @('ireland/','stockholm/','london/') +$IntPaths = @('bahrain/','guam/','mumbai/','tokyo/') + +if ($Region -eq 'ALL') { $LatestPaths = $OCEPaths + $USPaths + $EUPaths + $IntPaths + $BRPaths } +elseif ($Region -eq 'US') { $LatestPaths = $USPaths } +elseif ($Region -eq 'BR') { $LatestPaths = $BRPaths } +elseif ($Region -eq 'EU') { $LatestPaths = $EUPaths } +elseif ($Region -eq 'OCE') { $LatestPaths = $OCEPaths } +elseif ($Region -eq 'INT') { $LatestPaths = $IntPaths } + +if (!$OutFolder) { $OutFolder = "$PSScriptRoot" } +if (!(Test-Path -LiteralPath "$OutFolder" )) { New-Item $OutFolder -ItemType Directory -Force | Out-Null } +$OutFolder = Get-Item -LiteralPath $OutFolder + +$timeUTC = (Get-Date).ToUniversalTime() + +if ($TargetDate) { + if ($TargetDate.GetType() -eq [string]) { $TargetDate = [datetime]::Parse($TargetDate) } + if ($TargetDate.GetType() -ne [datetime]) { 'ERROR: -StartDate invalid'; return } +} + +if ($LimitDate) { + if ($LimitDate.getType() -eq [string]) { $LimitDate = [datetime]::Parse($LimitDate) } + if ($LimitDate.GetType() -ne [datetime]) { 'ERROR: -EndDate invalid'; return } + + if ($TargetDate -gt $LimitDate) { + $temp = $TargetDate + $TargetDate = $LimitDate + $LimitDate = $temp + Remove-Variable temp + } +} + +if (!$FilterPath -and ` + !$Region ) { $FilterPath = 'sydney/staging/' } + +if ($FilterPath -match '^(.*[\\/])+(.*\.json)$') { $FilterPath = $matches[1]; $FilterFile = $matches[2] } +elseif (!$FilterFile -and $FilterPath) { $FilterPath = (($FilterPath -split ',' | foreach { if ($_ -notmatch '.*/$') { "$_/" } else { $_ } }) -join ',') } +#if ($FilterPath -match '/.*') { $FilterPath = $FilterPath.TrimStart("/") } + +function New-UrlStatFile { return [PSCustomObject]@{ Name=$args[0]; DateTime=$args[1]; Size=$args[2] } } + +if (!$LimitDate -and $LimitMins -eq 0 -and $LimitDays -eq 0) { + if ($LocalFile) { $LimitDays = 90 } + elseif ($FilterFile) { $LimitDays = 30 } + elseif ($LatestFile) { $LimitDays = 7 } + else { $LimitDays = 1 } +} + +if (!$TargetDate) { $TargetDate = $timeUTC.AddMinutes($LimitMins * -1).AddDays($LimitDays * -1) } +if (!$LimitDate) { $LimitDate = $timeUTC } + +if ($LocalFile) { + if (!(Test-Path -LiteralPath "$OutFolder$LocalFile")) { write-host "ERROR - Local File not found:- $LocalFile"; return } + elseif ($LocalFile -notmatch '.*/(20[1-3][0-9]-[0-1][0-9]-[0-3][0-9][-_][0-9][0-9]-[0-5][0-9]-[0-5][0-9]).*[.]json$') { $matches; write-host "ERROR - No date found in '$LocalFile'"; return } + else { + $f_date = ([DateTime]::ParseExact(($matches[1] -replace '_','-'),'yyyy-MM-dd-HH-mm-ss',$null)) + if ($f_date -lt $TargetDate -or $f_date -gt $LimitDate) { write-host "ERROR - Did not meet date/time restrictions:- $LocalFile"; return} + } + $filesDownloaded = @(Get-Item -LiteralPath "$OutFolder$LocalFile") + write-host "====================================================================================================" + write-host " Processing local file: $LocalFile" +} else { + if ($Region) { + if (!$FilterPath) { $FilterPath = 'quad/,staging/,scrim/,tourney/' } + foreach ($lp in $LatestPaths) { + foreach ($fp in ($FilterPath -split ',')) { $temp += "$(if ($temp) { ',' })$lp$fp" } + } + $FilterPath = $temp + } + $statFiles = @() + + if ($FilterFile -and $FilterFile -notmatch '\*') { + $statFiles = New-UrlStatFile "$FilterPath$FilterFile" $null -1 + } elseif (!$AwsCLI) { + foreach ($p in ($FilterPath -split ',')) { + $tempDate = $TargetDate + while ($tempDate -le $LimitDate) { + $xml = [xml](invoke-webrequest -Uri "$($AwsUrl)?prefix=$p$($tempDate.Year)-$('{0:d2}' -f $tempDate.Month)") + $xml.ListBucketResult.Contents | foreach { if ($_) { $statFiles += (New-UrlStatFile $_.Key $_.LastModified $_.Size) } } + $tempDate = $tempDate.AddMonths(1) + if ($tempDate -gt $LimitDate -and $TempDate.Month -eq $LimitDate.Month) { $tempDate = $LimitDate } + } + } + } else { + $statJson = (& aws s3api list-objects-v2 --bucket fortressone-stats --query "Contents[?LastModified>``$($TargetDate.ToString('yyyy-MM-ddTHH:mm:ss'))``]") | ConvertFrom-Json + $statFiles += $statJson | Where-Object { $_.Key -match '.*/(quad|staging|scrim|tourney|fo|hue)/.*\.json$' } | foreach { (New-UrlStatFile $_.Key $_.LastModified $_.Size) } + } + + + #LatestFileOnly + if ($LatestFile) { $statFiles = ($statFiles | Sort-Object DateTime -Descending)[0] } + + write-host "FO Stats Downloader | $(Get-Date)`n"` + "-------------------------------------------------------------`n"` + "-AwsCLI:`t$AwsCLI`n"` + "-TargetDate:`t$('{0:yyyy-MM-dd-HH-mm-ss}' -f $TargetDate)`n"` + "-LimitDate: `t$('{0:yyyy-MM-dd-HH-mm-ss}' -f $LimitDate)`n"` + "-LimitDays:`t$LimitDays`n" ` + "-LimitMins:`t$LimitMins`n" ` + "-FilterPath:`t$FilterPath`n" ` + "-FilterFile:`t$FilterFile`n" ` + "-OutFolder:`t$OutFolder`n"` + "Options:`t$(@('CleanUp','DownloadOnly','Overwrite','ForceStats') | foreach { if (Get-Variable $_ -ValueOnly -ErrorAction SilentlyContinue) { "-$_" }})`n"` + "Stats:`t$(@('RoundTime','TextSave','NoStatJson','TextOnly','OpenHTML') | foreach { if (Get-Variable $_ -ValueOnly -ErrorAction SilentlyContinue) { "-$_" }})`n"` + "Batches:`t$(@('FullBatch','NewOnlyBatch','DailyBatch','MonthlyBatch','PeriodBatch','PeriodExpire') | foreach { if (Get-Variable $_ -ValueOnly -ErrorAction SilentlyContinue) { "-$_" }})`n"` + "=============================================================`n" + + + + $filesDownloaded = @() + + write-host " Downloading..." + write-host "====================================================================================================" + + foreach ($f in $statFiles) { + if ($FilterFile -match '\*' -and $f.Name -notlike "*$($FilterFile)*") { continue } + if (!($FileFilter) -and (!$AwsCLI -and ($LimitMins -gt 0 -or $LimitDays -gt 0 -or $LimitDate))) { + if ($f.Name -notmatch '20[1-3][0-9]-[0-1][0-9]-[0-3][0-9]-[0-9][0-9]-[0-5][0-9]-[0-5][0-9]') { + Write-Host "ERROR: Minute/Day limit not possible - file has invalid date/time [$($f.Name)]" + continue + } else { + $f_date = ([DateTime]::ParseExact(($matches[0] -replace '_','-'),'yyyy-MM-dd-HH-mm-ss',$null)) + if ($f_date -lt $TargetDate -or $f_date -gt $LimitDate) { continue } + } + + } + + $filePath = "$OutFolder\$(Split-Path $f.Name)" + $fileName = "$OutFolder\$($f.Name)" + + if (!$Overwrite -and ( (Test-Path -LiteralPath ($fileName -replace '\.json$','.html')) ` + -or ((Test-Path -LiteralPath $fileName) -and (Get-Item -LiteralPath $fileName).LastWriteTime -gt (Get-Date).AddMinutes(-20)) ) ` + ) { + write-host "SKIPPED: File Already exists [$($f.Name)]" + if ($ForceStats) { $filesDownloaded += (Get-Item -LiteralPath $fileName) } + $filesSkipped += 1 + continue + } + + if (!(Test-Path -LiteralPath $filePath)) { New-Item -Path $filePath -ItemType Directory | Out-Null } + write-host "Downloading:- $($f.Name)" + + foreach ($retry in 5..0) { + try { + (invoke-webrequest -Uri "$($AwsUrl)$($f.Name)").Content | Out-File -LiteralPath $fileName -Encoding utf8 + $filesDownloaded += (Get-Item -LiteralPath $fileName) + break + } catch { + Write-Host "Error:- $($_.Exception.Message)" + Write-Host "URL:- $($AwsUrl)$($f.Name)" + # Retry for single file downloads only + if (!$FilterFile -and $FilterFile -match '\*') { break } + if ($retry -gt 0) { + Write-Host "Retrying in 3 seconds - $retry remaining" + Start-Sleep -Seconds 3 + } + } + } + } + + if ($filesDownloaded.Count -eq 0) { + write-host "----------------------------------------------------------------------------------------------------" + Write-Host "No stat files matched from the $($statFiles.Count) AWSresults filtered." + Write-Host "NOTE: AWS results are capped at 1000 files, limit your days/mins or paths." + Write-Host "" + Write-Host "`tFirst file date:`t$(if ($statFiles.Count -gt 0) { ($statFiles.DateTime | Measure-Object -Minimum).Minimum } else { 'No results' } )" + Write-Host "`tLast file date:`t$(if ($statFiles.Count -gt 0) { ($statFiles.DateTime | Measure-Object -Maximum).Maximum } else { 'No results' } )" + Write-Host "" + Write-Host "Please check your search filters and try again." + write-host "====================================================================================================" + } else { write-host "====================================================================================================" } +} + +if (!$DownloadOnly -and !$Demos -and $filesDownloaded.Count -gt 0) { + foreach ($fileName in $filesDownloaded) { + $param = @{ StatFile=$fileName } + if ($RoundTime) { $param.RoundTime = $RoundTime } + if ($TextOnly) { $param.TextOnly = $true } + if ($TextSave) { $param.TextSave = $true } + if ($NoStatJson) { $param.NoStatJson = $true } + if ($OpenHTML) { $param.OpenHTML = $true } + + $i++ + write-host "FO Stats ($i of $($filesDownloaded.Length)):- `t$($fileName)" + if ($param.Count -gt 1) { Write-Host "Parameters: $($param.GetEnumerator() | foreach { if ($_.Name -ne 'StatFile') { " -$($_.Name): $($_.Value)" } })" } + write-host "----------------------------------------------------------------------------------------------------" + & $PSScriptRoot\FO_stats_v2.ps1 @param + + if (!$NoStatJson) { + $outJson = (Get-Content -LiteralPath ($fileName -replace '\.json$','_stats.json') -Raw) | ConvertFrom-Json + #$outJson.Matches[0].Match = "$($fileName.Directory.Parent.Name)/$($fileName.Directory.Name)/$($outJson.Matches[0].Match)" + #Rebuild the Matches entry so server is the first column + $outJson.Matches[0] = [pscustomobject]@{ Server = "$($fileName.Directory.Parent.Name)/$($fileName.Directory.Name)/"; + Match = $outJson.Matches[0].Match; + Winner = $outJson.Matches[0].Winner; + Rating = $outJson.Matches[0].Rating; + Score1 = $outJson.Matches[0].Score1; + Team1 = $outJson.Matches[0].Team1; + Score2 = $outJson.Matches[0].Score2; + Team2 = $outJson.Matches[0].Team2; + } + ($outJson | ConvertTo-JSON) | Out-File -LiteralPath ($fileName -replace '\.json$','_stats.json') -Encoding utf8 + } + write-host "----------------------------------------------------------------------------------------------------" + write-host "FO Stats Completed:-`t$($fileName)" + write-host "----------------------------------------------------------------------------------------------------" + if ($CleanUp) { Remove-Item -LiteralPath $fileName.FullName -Force } + } + write-host "====================================================================================================" +} + +if ($FullBatch -or $NewOnlyBatch) { + $DayFilterOCE = [datetime]::Parse('19:00') # Syd 6am + $DayFilterUS = [datetime]::Parse('14:00') # Cali 6am + $DayFilterBR = [datetime]::Parse('18:00') # Brasil 6am + $DayFilterEU = [datetime]::Parse('6:00') # UTC time + $DayFilterINT = [datetime]::Parse('16:00') # Dead zone for all regions - 3am Syd + + #OCE 19-23 +1 day, 00-18 Same day, 13-18 6am grace period + if ([DateTime]::UtcNow.hour -in 19..23) { $DayReportOCE = $DayFilterOCE.AddDays(1) } + elseif ([DateTime]::UtcNow.hour -in 0..12) { $DayReportOCE = $DayFilterOCE; $DayFilterOCE = $DayFilterOCE.AddDays(-1) } + elseif ([DateTime]::UtcNow.hour -in 13..18) { $DayReportOCE = $DayFilterOCE; $DayFilterOCE = $DayFilterOCE.AddDays(-1) } + + #US 14-23 same day, 0-7 +1 day, 8-14 6am grace period + if ([DateTime]::UtcNow.hour -in 14..23) { $DayReportUS = $DayFilterUS } + elseif ([DateTime]::UtcNow.hour -in 0..7) { $DayFilterUS = $DayFilterUS.AddDays(-1); $DayReportUS = $DayFilterUS } + elseif ([DateTime]::UtcNow.hour -in 8..13) { $DayFilterUS = $DayFilterUS.AddDays(-1); $DayReportUS = $DayFilterUS } + + #BR 14-23 same day, 0-7 +1 day, 8-14 6am grace period + if ([DateTime]::UtcNow.hour -in 18..23) { $DayReportBR = $DayFilterBR } + elseif ([DateTime]::UtcNow.hour -in 0..12) { $DayFilterBR = $DayFilterBR.AddDays(-1); $DayReportBR = $DayFilterBR } + elseif ([DateTime]::UtcNow.hour -in 13..17) { $DayFilterBR = $DayFilterBR.AddDays(-1); $DayReportBR = $DayFilterBR } + + + #EU UTC time, 0-6 6am grace period + if ([DateTime]::UtcNow.hour -in 0..6) { $DayFilterEU = $DayFilterEU.AddDays(-1) } + $DayReportEU = $DayFilterEU + + # Interational cut-off, new days starts at 4pm + if ([DateTime]::UtcNow.hour -in 0..15) { $DayFilterINT = $DayFilterINT.AddDays(-1) } + $DayReportINT = $DayFilterINT + + if ($FullBatch) { + & $PSScriptRoot\FO_stats_join-json.ps1 -StartOffSetDays 7 -Region ALL -FilterPath 'tourney/' -PlayerCount '^4$' -ExcludeFile "$PSScriptRoot/.2v2_tourney_exlude.txt" -OutFile "$PSScriptRoot/2v2_tourney_stats.json" + + #Scan for all files and refresh stats + if ($DailyBatch) { + #Daily stats cumalitve + & $PSScriptRoot\FO_stats_join-json.ps1 -StartDateTime $DayFilterOCE.ToString() -Region OCE -OutFile "$PSScriptRoot/_daily/oceania/oceania_DailyStats_$('{0:yyyy-MM-dd}' -f $DayReportOCE).json" + & $PSScriptRoot\FO_stats_join-json.ps1 -StartDateTime $DayFilterUS.ToString() -Region US -OutFile "$PSScriptRoot/_daily/north-america/north-america_DailyStats_$('{0:yyyy-MM-dd}' -f $DayReportUS).json" + & $PSScriptRoot\FO_stats_join-json.ps1 -StartDateTime $DayFilterBR.ToString() -Region BR -OutFile "$PSScriptRoot/_daily/brasil/brasil_DailyStats_$('{0:yyyy-MM-dd}' -f $DayReportBR).json" + & $PSScriptRoot\FO_stats_join-json.ps1 -StartDateTime $DayFilterEU.ToString() -Region EU -OutFile "$PSScriptRoot/_daily/europe/europe_DailyStats_$('{0:yyyy-MM-dd}' -f $DayReportEU).json" + & $PSScriptRoot\FO_stats_join-json.ps1 -StartDateTime $DayReportINT.ToString() -Region INT -OutFile "$PSScriptRoot/_daily/international/international_DailyStats_$('{0:yyyy-MM-dd}' -f $DayReportINT).json" + } + + if ($MonthlyBatch) { + #Monthly stats cumaltive + & $PSScriptRoot\FO_stats_join-json.ps1 -StartDateTime $DayFilterOCE.ToString() -Region OCE -OutFile "$PSScriptRoot/_monthly/oceania/oceania_MonthlyStats_$('{0:yyyy-MM}' -f $DayReportOCE).json" + & $PSScriptRoot\FO_stats_join-json.ps1 -StartDateTime $DayFilterUS.ToString() -Region US -OutFile "$PSScriptRoot/_monthly/north-america/north-america_MonthlyStats_$('{0:yyyy-MM}' -f $DayReportUS).json" + & $PSScriptRoot\FO_stats_join-json.ps1 -StartDateTime $DayFilterBR.ToString() -Region BR -OutFile "$PSScriptRoot/_monthly/brasil/brasil_MonthlyStats_$('{0:yyyy-MM}' -f $DayReportBR).json" + & $PSScriptRoot\FO_stats_join-json.ps1 -StartDateTime $DayFilterEU.ToString() -Region EU -OutFile "$PSScriptRoot/_monthly/europe/europe_MonthlyStats_$('{0:yyyy-MM}' -f $DayReportEU).json" + & $PSScriptRoot\FO_stats_join-json.ps1 -StartDateTime $DayReportINT.ToString() -Region INT -OutFile "$PSScriptRoot/_monthly/international/international_MonthlyStats_$('{0:yyyy-MM}' -f $DayReportINT).json" + } + } else { + # Add newly processed files only - i.e. $filesDownloaded + foreach ($fileName in $filesDownloaded) { + switch (((GetPathFromFileName $fileName.FullName) -split '/')[0] + '/') { + {$_ -in $OCEPaths} { $f_region = 'oceania'; $f_dayreport = $DayReportOCE; $f_dayfilter = $DayFilterOCE } + {$_ -in $USPaths} { $f_region = 'north-america'; $f_dayreport = $DayReportUS ; $f_dayfilter = $DayFilterUS } + {$_ -in $BRPaths} { $f_region = 'brasil'; $f_dayreport = $DayReportBR ; $f_dayfilter = $DayFilterBR } + {$_ -in $EUPaths} { $f_region = 'europe'; $f_dayreport = $DayReportEU ; $f_dayfilter = $DayFilterEU } + {$_ -in $INTPaths} { $f_region = 'international'; $f_dayreport = $DayReportINT; $f_dayfilter = $DayFilterINT } + else { continue } + } + + $fileName = $fileName.FullName -replace '_red.json$','_red_stats.json' + + if ($fileName.FullName -match '[\\/]tourney[\\/]') { + & $PSScriptRoot\FO_stats_join-json.ps1 -FilterPath $fileName -StartOffSetDays 1 -ExcludeFile "$PSScriptRoot/.2v2_tourney_exlude.txt" -OutFile "$PSScriptRoot/2v2_tourney_stats.json" + } + + & $PSScriptRoot\FO_stats_join-json.ps1 -FilterPath $fileName -StartDateTime $f_dayfilter.ToString() -OutFile "$PSScriptRoot/_daily/$f_region/$($f_region)_DailyStats_$('{0:yyyy-MM-dd}' -f $f_dayreport).json" + & $PSScriptRoot\FO_stats_join-json.ps1 -FilterPath $fileName -StartDateTime $f_dayfilter.ToString() -OutFile "$PSScriptRoot/_monthly/$f_region/$($f_region)_MonthlyStats_$('{0:yyyy-MM}' -f $f_dayreport).json" + & $PSScriptRoot\FO_stats_join-json.ps1 -FilterPath $fileName -StartOffSetDays 1 -OutFile "$PSScriptRoot/_stats-last24hrs.json" + & $PSScriptRoot\FO_stats_join-json.ps1 -FilterPath $fileName -StartOffsetDays 7 -OutFile "$PSScriptRoot/_stats-last7days.json" + } + } +} + +if ($PeriodBatch) { + Remove-Item "$PSScriptRoot/_stats-last24hrs.json" + Remove-Item "$PSScriptRoot/_stats-last7days.json" + & $PSScriptRoot\FO_stats_join-json.ps1 -StartOffSetDays 1 -Region ALL -OutFile "$PSScriptRoot/_stats-last24hrs.json" + & $PSScriptRoot\FO_stats_join-json.ps1 -StartOffSetDays 7 -Region ALL -OutFile "$PSScriptRoot/_stats-last7days.json" +} elseif ($PeriodExpire) { + write-host ' FO Downlaoder -PeriodExpire ' + write-host '---------------------------------------------' + + $json = (Get-Content -LiteralPath "$PSScriptRoot/_stats-last24hrs.json" -Raw) | ConvertFrom-Json + foreach ($m in $json.Matches) { + if ($m.Match -match '(\d{4}-\d\d-\d\d)[_-](\d\d-\d\d-\d\d)_') { + $dt = [datetime]::Parse($matches[1] + " " + ($matches[2] -replace '-',':')) + if ($dt -lt (Get-Date).AddDays(-1).ToUniversalTime()) { + & $PSScriptRoot/FO_stats_join-json.ps1 -RemoveMatch "$PSScriptRoot/$($m.Server)$($m.Match)_blue_vs_red(_vs_yell_vs_gren)?_stats.json" -FromJson "$PSScriptRoot/_stats-last24hrs.json" + } + } + } + $json = (Get-Content -LiteralPath "$PSScriptRoot/_stats-last7days.json" -Raw) | ConvertFrom-Json + foreach ($m in $json.Matches) { + if ($m.Match -match '(\d{4}-\d\d-\d\d)-(\d\d-\d\d-\d\d)_') { + $dt = [datetime]::Parse($matches[1] + " " + ($matches[2] -replace '-',':')) + + if ($dt -lt (Get-Date).AddDays(-7).ToUniversalTime()) { + & $PSScriptRoot/FO_stats_join-json.ps1 -RemoveMatch "$PSScriptRoot/$($m.Server)$($m.Match)_blue_vs_red(_vs_yell_vs_gren)?_stats.json" -FromJson "$PSScriptRoot/_stats-last7days.json" + } + } + } + write-host '=============================================' +} + +Write-host " FO Downloader complete | $(get-date)" + +# SIG # Begin signature block +# MIIboAYJKoZIhvcNAQcCoIIbkTCCG40CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB +# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR +# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUoeMZs+HX3LhMNBF8eBA/6Qtn +# OlGgghYVMIIDCDCCAfCgAwIBAgIQVxZN0cTEa7NFKtjIhSbFETANBgkqhkiG9w0B +# AQsFADAcMRowGAYDVQQDDBFIYXplIEF1dGhlbnRpY29kZTAeFw0yMzAyMTAwNTM3 +# MzRaFw0yNDAyMTAwNTU3MzRaMBwxGjAYBgNVBAMMEUhhemUgQXV0aGVudGljb2Rl +# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1M46e4LcpTytNDpPe4AN +# aUJrafdLCl23kH5G7qTsBRRwI6qpRhZc5TBI19oEwulC4t8h7nI6D78kYx8FdI2h +# tb0wWmhcPBAAT7iywe0G0Q3NgZxZKOyI0yto69Z7TMPnbGKhCegPuvAT0LejgTrK +# +OAH0a/uGBVCGgu1EsIOtVitWsuxTKNR5bX3b2Zoc1xaEVOMFGy74IvXzIx+VyaN +# pSH6JYo3iSLWmQNRMBvMPsRfcvkh9R1DXemAJX3LHEm0Bei3xco+20zhRQtCO1Md +# rAEAL3aq3oFDQ2KV1RCNqo5kvnwBBDumAveg7JnjuUd3DvqCF0+Hs+q2KjRy02vt +# MQIDAQABo0YwRDAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMw +# HQYDVR0OBBYEFFwWkpE7S3mEJiqQ8mTrbI0pUNm+MA0GCSqGSIb3DQEBCwUAA4IB +# AQC9XvfYZgwF/9CysiCloHccqkH8T+AJJCX1Uq1lZ9DTO7olZfTmrI/JQWrSDdrK +# hsEi9wp6uTjfapE18EQW9CBIAMNLFjhFv/uLGEp9vZpUWpuG+2hqVu0thtZbw/Gm +# gMh8yhm/lTXUj1tcltPDkgWnd2u44O2fdF2kE6hevBEXM71a45OiMic3SgpLi6m3 +# nMrsdQ0wu3qDm5I2Tm/Htq7Telmq0V3Lu1CKK6lYxxR1+Epsxumyiu0q9IKeKLMR +# RlbKKqHPcKoOadWNfZ1kR+5sO4q3+Tnc1evHS4HbJHQVZtR2k/tdsOvNi8qk9Sh9 +# +GFTkJDGhZR9TsyW2Se2KTUMMIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAY +# WjANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl +# cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdp +# Q2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5 +# MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkw +# FwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVz +# dGVkIFJvb3QgRzQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBz +# aN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbr +# VsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTR +# EEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJ +# z82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyO +# j4DatpGYQJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6R +# AXwhTNS8rhsDdV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k +# 98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJ +# tppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUa +# dmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZB +# dd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVf +# nSD8oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0T +# AQH/BAUwAwEB/zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0j +# BBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsG +# AQUFBwEBBG0wazAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29t +# MEMGCCsGAQUFBzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNl +# cnRBc3N1cmVkSURSb290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9j +# cmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYD +# VR0gBAowCDAGBgRVHSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3Qb +# PbYW1/e/Vwe9mqyhhyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5 +# +KH38nLeJLxSA8hO0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+n +# BgMTdydE1Od/6Fmo8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc +# /RzY9HdaXFSMb++hUD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVr +# zyerbHbObyMt9H5xaiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o +# 4rmUMIIGrjCCBJagAwIBAgIQBzY3tyRUfNhHrP0oZipeWzANBgkqhkiG9w0BAQsF +# ADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL +# ExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJv +# b3QgRzQwHhcNMjIwMzIzMDAwMDAwWhcNMzcwMzIyMjM1OTU5WjBjMQswCQYDVQQG +# EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0 +# IFRydXN0ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMIICIjAN +# BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxoY1BkmzwT1ySVFVxyUDxPKRN6mX +# UaHW0oPRnkyibaCwzIP5WvYRoUQVQl+kiPNo+n3znIkLf50fng8zH1ATCyZzlm34 +# V6gCff1DtITaEfFzsbPuK4CEiiIY3+vaPcQXf6sZKz5C3GeO6lE98NZW1OcoLevT +# sbV15x8GZY2UKdPZ7Gnf2ZCHRgB720RBidx8ald68Dd5n12sy+iEZLRS8nZH92GD +# Gd1ftFQLIWhuNyG7QKxfst5Kfc71ORJn7w6lY2zkpsUdzTYNXNXmG6jBZHRAp8By +# xbpOH7G1WE15/tePc5OsLDnipUjW8LAxE6lXKZYnLvWHpo9OdhVVJnCYJn+gGkcg +# Q+NDY4B7dW4nJZCYOjgRs/b2nuY7W+yB3iIU2YIqx5K/oN7jPqJz+ucfWmyU8lKV +# EStYdEAoq3NDzt9KoRxrOMUp88qqlnNCaJ+2RrOdOqPVA+C/8KI8ykLcGEh/FDTP +# 0kyr75s9/g64ZCr6dSgkQe1CvwWcZklSUPRR8zZJTYsg0ixXNXkrqPNFYLwjjVj3 +# 3GHek/45wPmyMKVM1+mYSlg+0wOI/rOP015LdhJRk8mMDDtbiiKowSYI+RQQEgN9 +# XyO7ZONj4KbhPvbCdLI/Hgl27KtdRnXiYKNYCQEoAA6EVO7O6V3IXjASvUaetdN2 +# udIOa5kM0jO0zbECAwEAAaOCAV0wggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYD +# VR0OBBYEFLoW2W1NhS9zKXaaL3WMaiCPnshvMB8GA1UdIwQYMBaAFOzX44LScV1k +# TN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcD +# CDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj +# ZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t +# L0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0 +# cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmww +# IAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUA +# A4ICAQB9WY7Ak7ZvmKlEIgF+ZtbYIULhsBguEE0TzzBTzr8Y+8dQXeJLKftwig2q +# KWn8acHPHQfpPmDI2AvlXFvXbYf6hCAlNDFnzbYSlm/EUExiHQwIgqgWvalWzxVz +# jQEiJc6VaT9Hd/tydBTX/6tPiix6q4XNQ1/tYLaqT5Fmniye4Iqs5f2MvGQmh2yS +# vZ180HAKfO+ovHVPulr3qRCyXen/KFSJ8NWKcXZl2szwcqMj+sAngkSumScbqyQe +# JsG33irr9p6xeZmBo1aGqwpFyd/EjaDnmPv7pp1yr8THwcFqcdnGE4AJxLafzYeH +# JLtPo0m5d2aR8XKc6UsCUqc3fpNTrDsdCEkPlM05et3/JWOZJyw9P2un8WbDQc1P +# tkCbISFA0LcTJM3cHXg65J6t5TRxktcma+Q4c6umAU+9Pzt4rUyt+8SVe+0KXzM5 +# h0F4ejjpnOHdI/0dKNPH+ejxmF/7K9h+8kaddSweJywm228Vex4Ziza4k9Tm8heZ +# Wcpw8De/mADfIBZPJ/tgZxahZrrdVcA6KYawmKAr7ZVBtzrVFZgxtGIJDwq9gdkT +# /r+k0fNX2bwE+oLeMt8EifAAzV3C+dAjfwAL5HYCJtnwZXZCpimHCUcr5n8apIUP +# /JiW9lVUKx+A+sDyDivl1vupL0QVSucTDh3bNzgaoSv27dZ8/DCCBsIwggSqoAMC +# AQICEAVEr/OUnQg5pr/bP1/lYRYwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMC +# VVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBU +# cnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yMzA3 +# MTQwMDAwMDBaFw0zNDEwMTMyMzU5NTlaMEgxCzAJBgNVBAYTAlVTMRcwFQYDVQQK +# Ew5EaWdpQ2VydCwgSW5jLjEgMB4GA1UEAxMXRGlnaUNlcnQgVGltZXN0YW1wIDIw +# MjMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCjU0WHHYOOW6w+VLMj +# 4M+f1+XS512hDgncL0ijl3o7Kpxn3GIVWMGpkxGnzaqyat0QKYoeYmNp01icNXG/ +# OpfrlFCPHCDqx5o7L5Zm42nnaf5bw9YrIBzBl5S0pVCB8s/LB6YwaMqDQtr8fwkk +# lKSCGtpqutg7yl3eGRiF+0XqDWFsnf5xXsQGmjzwxS55DxtmUuPI1j5f2kPThPXQ +# x/ZILV5FdZZ1/t0QoRuDwbjmUpW1R9d4KTlr4HhZl+NEK0rVlc7vCBfqgmRN/yPj +# yobutKQhZHDr1eWg2mOzLukF7qr2JPUdvJscsrdf3/Dudn0xmWVHVZ1KJC+sK5e+ +# n+T9e3M+Mu5SNPvUu+vUoCw0m+PebmQZBzcBkQ8ctVHNqkxmg4hoYru8QRt4GW3k +# 2Q/gWEH72LEs4VGvtK0VBhTqYggT02kefGRNnQ/fztFejKqrUBXJs8q818Q7aESj +# pTtC/XN97t0K/3k0EH6mXApYTAA+hWl1x4Nk1nXNjxJ2VqUk+tfEayG66B80mC86 +# 6msBsPf7Kobse1I4qZgJoXGybHGvPrhvltXhEBP+YUcKjP7wtsfVx95sJPC/QoLK +# oHE9nJKTBLRpcCcNT7e1NtHJXwikcKPsCvERLmTgyyIryvEoEyFJUX4GZtM7vvrr +# kTjYUQfKlLfiUKHzOtOKg8tAewIDAQABo4IBizCCAYcwDgYDVR0PAQH/BAQDAgeA +# MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwIAYDVR0gBBkw +# FzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMB8GA1UdIwQYMBaAFLoW2W1NhS9zKXaa +# L3WMaiCPnshvMB0GA1UdDgQWBBSltu8T5+/N0GSh1VapZTGj3tXjSTBaBgNVHR8E +# UzBRME+gTaBLhklodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz +# dGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5nQ0EuY3JsMIGQBggrBgEFBQcB +# AQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFgG +# CCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRU +# cnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3 +# DQEBCwUAA4ICAQCBGtbeoKm1mBe8cI1PijxonNgl/8ss5M3qXSKS7IwiAqm4z4Co +# 2efjxe0mgopxLxjdTrbebNfhYJwr7e09SI64a7p8Xb3CYTdoSXej65CqEtcnhfOO +# HpLawkA4n13IoC4leCWdKgV6hCmYtld5j9smViuw86e9NwzYmHZPVrlSwradOKmB +# 521BXIxp0bkrxMZ7z5z6eOKTGnaiaXXTUOREEr4gDZ6pRND45Ul3CFohxbTPmJUa +# VLq5vMFpGbrPFvKDNzRusEEm3d5al08zjdSNd311RaGlWCZqA0Xe2VC1UIyvVr1M +# xeFGxSjTredDAHDezJieGYkD6tSRN+9NUvPJYCHEVkft2hFLjDLDiOZY4rbbPvlf +# sELWj+MXkdGqwFXjhr+sJyxB0JozSqg21Llyln6XeThIX8rC3D0y33XWNmdaifj2 +# p8flTzU8AL2+nCpseQHc2kTmOt44OwdeOVj0fHMxVaCAEcsUDH6uvP6k63llqmjW +# Iso765qCNVcoFstp8jKastLYOrixRoZruhf9xHdsFWyuq69zOuhJRrfVf8y2OMDY +# 7Bz1tqG4QyzfTkx9HmhwwHcK1ALgXGC7KP845VJa1qwXIiNO9OzTF/tQa/8Hdx9x +# l0RBybhG02wyfFgvZ0dl5Rtztpn5aywGRu9BHvDwX+Db2a2QgESvgBBBijGCBPUw +# ggTxAgEBMDAwHDEaMBgGA1UEAwwRSGF6ZSBBdXRoZW50aWNvZGUCEFcWTdHExGuz +# RSrYyIUmxREwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAw +# GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisG +# AQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFEvlTHYMUvxXZkMrOgSiD+Qz+sONMA0G +# CSqGSIb3DQEBAQUABIIBAG5rqgCt3aO8ThRjyRunYsaKcb4J9CI/qshYjprMFX0E +# nSOnp6sHoBDdcKqDG56riyHRGmIVViMUSfeM0xIKTCxQLfujfr0E5vJmxrNpPnNa +# WleULZ33OPspj8oKwebdJH5BvQqio3/OW66ThtvljxibFFJkHFYwPT8V3AgPFn1D +# YaZ+xN8t3qZik8wjTu1s7MdN7MGG6zFAL8pKR9T+YfvtjZgzLXthQW+meFnWnUUE +# 1zszdGiMvKZSf1tugqx6GMeKik9Jhd0Rlkr2heYQ5Fo2hcrdNYLIRnURu0Mwqxt5 +# Cpb0bo7cu57qDCRR+NtCzcCqUAKubHj58bYxDQnt+QOhggMgMIIDHAYJKoZIhvcN +# AQkGMYIDDTCCAwkCAQEwdzBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNl +# cnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNBNDA5NiBT +# SEEyNTYgVGltZVN0YW1waW5nIENBAhAFRK/zlJ0IOaa/2z9f5WEWMA0GCWCGSAFl +# AwQCAQUAoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUx +# DxcNMjMwODIyMDUyODI3WjAvBgkqhkiG9w0BCQQxIgQgA9/+CFmwhhb7IthV66eW +# RnJecNchS4x1PQUDx09kiAYwDQYJKoZIhvcNAQEBBQAEggIAXjEuPtu6aVV+wKFG +# eMBwtRmbu3HvzD3M07pZsyJJ+/nILYtKSGKzYYwxhx1aSeEjKizapLTzQt6dvOCj +# voG0GmAF94XLMM37TKQ0wl7wsmjaRkZmKjkY6wLDBuqu9McZlJSl5R7HlVqrI+wj +# ZDTPmLBQMVarya6QbGgMBdccVHjObCHuxW3p6dyTMt6mcdtbhWJkCosLjvgCDOXk +# QdTfjGkBmRfkviuhmSHFWm/RzGDVht2IZ8tsKA/EA3GX5/xLfZsBgqge0zDF6Zag +# 9JtNdQAqvTpXiZbyr028p/wsPI/mFAY3C5MtCSTXiCJUoQg8jQQ9B2MpHzCgnJg0 +# i8Jrg+mrVP+2wunL12O82teXakjsA0GHb1x123U2uvHCu5tciZbi3nzNhkUMAy3d +# +azRMG0gwrPJHup1/FwUHdCkMuIYN5X5kVudXSoi6bystyZcal8d8maAm5OLvGaO +# fhJQGMHnxDpOGo2aF0kyxCg+D9IKdifGj5/aWm0tsLwFUDw5dzrRA1GWWK7BPd4b +# OBXe+/O6u3mlhZeoIYWiiSjkfQcLHrWGbHl8hRkmtzWUKpXcevZ1vvyYvHYm5HwK +# SL1kBzGVy46xy8gW8EcG1yO4cdyJOzRZ/kbGqhzQ7w4V1eFHZXxduupH3de+OYmL +# LNHSytJUT8bXVpZAXH71UWmXwO4= +# SIG # End signature block diff --git a/backup20240621.zip b/backup20240621.zip new file mode 100644 index 0000000..a82fd61 Binary files /dev/null and b/backup20240621.zip differ diff --git a/backupH_20230829.zip b/backupH_20230829.zip new file mode 100644 index 0000000..9ea9d69 Binary files /dev/null and b/backupH_20230829.zip differ diff --git a/backup_231012.zip b/backup_231012.zip new file mode 100644 index 0000000..54c4f5e Binary files /dev/null and b/backup_231012.zip differ diff --git a/fo_downloader_shortcuts/z_ALL_demos_12hr.bat b/fo_downloader_shortcuts/z_ALL_demos_12hr.bat new file mode 100644 index 0000000..44a2edf Binary files /dev/null and b/fo_downloader_shortcuts/z_ALL_demos_12hr.bat differ diff --git a/fo_downloader_shortcuts/z_ALL_demos_24hr.bat b/fo_downloader_shortcuts/z_ALL_demos_24hr.bat new file mode 100644 index 0000000..83dff2d --- /dev/null +++ b/fo_downloader_shortcuts/z_ALL_demos_24hr.bat @@ -0,0 +1,23 @@ +cd /D %~dp0 +powershell -File "%~dp0_FoDownloader.ps1" -LimitDays 1 -Region ALL -Demos +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_ALL_demos_LatestFile.bat b/fo_downloader_shortcuts/z_ALL_demos_LatestFile.bat new file mode 100644 index 0000000..6a3fa5d --- /dev/null +++ b/fo_downloader_shortcuts/z_ALL_demos_LatestFile.bat @@ -0,0 +1,24 @@ +cd /D %~dp0 +powershell -file "%~dp0_FoDownloader.ps1" -LatestFile -Region ALL -Demos +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_ALL_stats_12hr.bat b/fo_downloader_shortcuts/z_ALL_stats_12hr.bat new file mode 100644 index 0000000..4614b1e Binary files /dev/null and b/fo_downloader_shortcuts/z_ALL_stats_12hr.bat differ diff --git a/fo_downloader_shortcuts/z_ALL_stats_24hr.bat b/fo_downloader_shortcuts/z_ALL_stats_24hr.bat new file mode 100644 index 0000000..ce003de --- /dev/null +++ b/fo_downloader_shortcuts/z_ALL_stats_24hr.bat @@ -0,0 +1,23 @@ +cd /D %~dp0 +powershell -File "%~dp0_FoDownloader.ps1" -LimitDays 1 -Region ALL +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_ALL_stats_LatestFile.bat b/fo_downloader_shortcuts/z_ALL_stats_LatestFile.bat new file mode 100644 index 0000000..8512fc4 --- /dev/null +++ b/fo_downloader_shortcuts/z_ALL_stats_LatestFile.bat @@ -0,0 +1,24 @@ +cd /D %~dp0 +powershell -file "%~dp0_FoDownloader.ps1" -LatestFile -Region ALL +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_EU_demos_12hr.bat b/fo_downloader_shortcuts/z_EU_demos_12hr.bat new file mode 100644 index 0000000..7f052e4 Binary files /dev/null and b/fo_downloader_shortcuts/z_EU_demos_12hr.bat differ diff --git a/fo_downloader_shortcuts/z_EU_demos_24hr.bat b/fo_downloader_shortcuts/z_EU_demos_24hr.bat new file mode 100644 index 0000000..7abe242 --- /dev/null +++ b/fo_downloader_shortcuts/z_EU_demos_24hr.bat @@ -0,0 +1,23 @@ +cd /D %~dp0 +powershell -File "%~dp0_FoDownloader.ps1" -LimitDays 1 -Region EU -Demos +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_EU_demos_LatestFile.bat b/fo_downloader_shortcuts/z_EU_demos_LatestFile.bat new file mode 100644 index 0000000..678e2fe --- /dev/null +++ b/fo_downloader_shortcuts/z_EU_demos_LatestFile.bat @@ -0,0 +1,24 @@ +cd /D %~dp0 +powershell -file "%~dp0_FoDownloader.ps1" -LatestFile -Region EU -Demos +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_EU_stats_12hr.bat b/fo_downloader_shortcuts/z_EU_stats_12hr.bat new file mode 100644 index 0000000..e49a27d Binary files /dev/null and b/fo_downloader_shortcuts/z_EU_stats_12hr.bat differ diff --git a/fo_downloader_shortcuts/z_EU_stats_24hr.bat b/fo_downloader_shortcuts/z_EU_stats_24hr.bat new file mode 100644 index 0000000..1373cdc --- /dev/null +++ b/fo_downloader_shortcuts/z_EU_stats_24hr.bat @@ -0,0 +1,23 @@ +cd /D %~dp0 +powershell -File "%~dp0_FoDownloader.ps1" -LimitDays 1 -Region EU +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_EU_stats_LatestFile.bat b/fo_downloader_shortcuts/z_EU_stats_LatestFile.bat new file mode 100644 index 0000000..1aa12a3 --- /dev/null +++ b/fo_downloader_shortcuts/z_EU_stats_LatestFile.bat @@ -0,0 +1,24 @@ +cd /D %~dp0 +powershell -file "%~dp0_FoDownloader.ps1" -LatestFile -Region EU +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_INT_demos_12hr.bat b/fo_downloader_shortcuts/z_INT_demos_12hr.bat new file mode 100644 index 0000000..12e9614 Binary files /dev/null and b/fo_downloader_shortcuts/z_INT_demos_12hr.bat differ diff --git a/fo_downloader_shortcuts/z_INT_demos_24hr.bat b/fo_downloader_shortcuts/z_INT_demos_24hr.bat new file mode 100644 index 0000000..c5ce92e --- /dev/null +++ b/fo_downloader_shortcuts/z_INT_demos_24hr.bat @@ -0,0 +1,23 @@ +cd /D %~dp0 +powershell -File "%~dp0_FoDownloader.ps1" -LimitDays 1 -Region INT -Demos +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_INT_demos_LatestFile.bat b/fo_downloader_shortcuts/z_INT_demos_LatestFile.bat new file mode 100644 index 0000000..ec5f8e5 --- /dev/null +++ b/fo_downloader_shortcuts/z_INT_demos_LatestFile.bat @@ -0,0 +1,24 @@ +cd /D %~dp0 +powershell -file "%~dp0_FoDownloader.ps1" -LatestFile -Region INT -Demos +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_INT_stats_12hr.bat b/fo_downloader_shortcuts/z_INT_stats_12hr.bat new file mode 100644 index 0000000..e4b6fa6 Binary files /dev/null and b/fo_downloader_shortcuts/z_INT_stats_12hr.bat differ diff --git a/fo_downloader_shortcuts/z_INT_stats_24hr.bat b/fo_downloader_shortcuts/z_INT_stats_24hr.bat new file mode 100644 index 0000000..37924a2 --- /dev/null +++ b/fo_downloader_shortcuts/z_INT_stats_24hr.bat @@ -0,0 +1,23 @@ +cd /D %~dp0 +powershell -File "%~dp0_FoDownloader.ps1" -LimitDays 1 -Region INT +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_INT_stats_LatestFile.bat b/fo_downloader_shortcuts/z_INT_stats_LatestFile.bat new file mode 100644 index 0000000..7a01143 --- /dev/null +++ b/fo_downloader_shortcuts/z_INT_stats_LatestFile.bat @@ -0,0 +1,24 @@ +cd /D %~dp0 +powershell -file "%~dp0_FoDownloader.ps1" -LatestFile -Region INT +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_OCE_demos_12hr.bat b/fo_downloader_shortcuts/z_OCE_demos_12hr.bat new file mode 100644 index 0000000..85f4dbc Binary files /dev/null and b/fo_downloader_shortcuts/z_OCE_demos_12hr.bat differ diff --git a/fo_downloader_shortcuts/z_OCE_demos_24hr.bat b/fo_downloader_shortcuts/z_OCE_demos_24hr.bat new file mode 100644 index 0000000..37a8e90 --- /dev/null +++ b/fo_downloader_shortcuts/z_OCE_demos_24hr.bat @@ -0,0 +1,23 @@ +cd /D %~dp0 +powershell -File "%~dp0_FoDownloader.ps1" -LimitDays 1 -Region OCE -Demos +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_OCE_demos_LatestFile.bat b/fo_downloader_shortcuts/z_OCE_demos_LatestFile.bat new file mode 100644 index 0000000..a7ab0ed --- /dev/null +++ b/fo_downloader_shortcuts/z_OCE_demos_LatestFile.bat @@ -0,0 +1,24 @@ +cd /D %~dp0 +powershell -file "%~dp0_FoDownloader.ps1" -LatestFile -Region OCE -Demos +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_OCE_stats_12hr.bat b/fo_downloader_shortcuts/z_OCE_stats_12hr.bat new file mode 100644 index 0000000..9a8488f Binary files /dev/null and b/fo_downloader_shortcuts/z_OCE_stats_12hr.bat differ diff --git a/fo_downloader_shortcuts/z_OCE_stats_24hr.bat b/fo_downloader_shortcuts/z_OCE_stats_24hr.bat new file mode 100644 index 0000000..fff838e --- /dev/null +++ b/fo_downloader_shortcuts/z_OCE_stats_24hr.bat @@ -0,0 +1,23 @@ +cd /D %~dp0 +powershell -File "%~dp0_FoDownloader.ps1" -LimitDays 1 -Region OCE +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_OCE_stats_LatestFile.bat b/fo_downloader_shortcuts/z_OCE_stats_LatestFile.bat new file mode 100644 index 0000000..9a28196 --- /dev/null +++ b/fo_downloader_shortcuts/z_OCE_stats_LatestFile.bat @@ -0,0 +1,24 @@ +cd /D %~dp0 +powershell -file "%~dp0_FoDownloader.ps1" -LatestFile -Region OCE +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_US_demos_12hr.bat b/fo_downloader_shortcuts/z_US_demos_12hr.bat new file mode 100644 index 0000000..b008700 Binary files /dev/null and b/fo_downloader_shortcuts/z_US_demos_12hr.bat differ diff --git a/fo_downloader_shortcuts/z_US_demos_24hr.bat b/fo_downloader_shortcuts/z_US_demos_24hr.bat new file mode 100644 index 0000000..d487065 --- /dev/null +++ b/fo_downloader_shortcuts/z_US_demos_24hr.bat @@ -0,0 +1,24 @@ +cd /D %~dp0 +powershell -File "%~dp0_FoDownloader.ps1" -LimitDays 1 -Region US -Demos +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_US_demos_LatestFile.bat b/fo_downloader_shortcuts/z_US_demos_LatestFile.bat new file mode 100644 index 0000000..fc33487 --- /dev/null +++ b/fo_downloader_shortcuts/z_US_demos_LatestFile.bat @@ -0,0 +1,25 @@ +cd /D %~dp0 +powershell -file "%~dp0_FoDownloader.ps1" -LatestFile -Region US -Demos +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_US_stats_12hr.bat b/fo_downloader_shortcuts/z_US_stats_12hr.bat new file mode 100644 index 0000000..61e339b Binary files /dev/null and b/fo_downloader_shortcuts/z_US_stats_12hr.bat differ diff --git a/fo_downloader_shortcuts/z_US_stats_24hr.bat b/fo_downloader_shortcuts/z_US_stats_24hr.bat new file mode 100644 index 0000000..c065f7e --- /dev/null +++ b/fo_downloader_shortcuts/z_US_stats_24hr.bat @@ -0,0 +1,24 @@ +cd /D %~dp0 +powershell -File "%~dp0_FoDownloader.ps1" -LimitDays 1 -Region US +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + + diff --git a/fo_downloader_shortcuts/z_US_stats_LatestFile.bat b/fo_downloader_shortcuts/z_US_stats_LatestFile.bat new file mode 100644 index 0000000..b361280 --- /dev/null +++ b/fo_downloader_shortcuts/z_US_stats_LatestFile.bat @@ -0,0 +1,25 @@ +cd /D %~dp0 +powershell -file "%~dp0_FoDownloader.ps1" -LatestFile -Region US +explorer %~dp0 +pause + + + + + + + + + + + + + + + + + + + + + diff --git a/http/_debian_pwsh-apache.txt b/http/_debian_pwsh-apache.txt new file mode 100644 index 0000000..dbaed24 --- /dev/null +++ b/http/_debian_pwsh-apache.txt @@ -0,0 +1,83 @@ + +sudo systemctl restart apache2 +sudo systemctl restart fostats-api.service +sudo nano /var/www/api/controller/controller.js + +--- +#Install Apache and Proxy +sudo apt update +sudo apt install apache2 +sudo a2enmod proxy +#sudo htpasswd -c ./.htpasswd foserver + +#Powershell +sudo apt update && sudo apt install -y curl gnupg apt-transport-https +curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - +sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-debian-bullseye-prod bullseye main" > /etc/apt/sources.list.d/microsoft.list' +sudo apt update && sudo apt install -y powershell + +#AWS CLI +curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" +unzip awscliv2.zip +sudo ./aws/install + +--- configure key/secret +aws configure + + +---- Cron tab. +# m h dom mon dow command +5,10,20,25,35,40,50,55 0-15,17-23 * * * pwsh /var/www/html/_FoDownloader.ps1 -Region ALL -AwsCLI -LimitDays 1 -DailyBatch > /var/www/html/.cron1.log +*/15 0-15,17-23 * * * pwsh /var/www/html/_FoDownloader.ps1 -Region ALL -AwsCLI -LimitDays 1 -DailyBatch -MonthlyBatch -FullBatch -PeriodExpire > /var/www/html/.cr> +0 16 * * * pwsh /var/www/html/_FoDownloader.ps1 -Region ALL -AwsCLI -LimitDays 30 -PeriodBatch -FullBatch > /var/www/html/.cron3.log +15,30,45 16 * * * pwsh /var/www/html/_FoDownloader.ps1 -Region ALL -AwsCLI -LimitDays 1 -DailyBatch -PeriodExpire > /var/www/html/.cron4.log + + + + +--------- +#backup +zip -r 1.zip . + +---- +#Apache CFG +sudo nano /etc/apache2/sites-available/000-default.conf + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + ProxyPass /notify http://127.0.0.1:8080/notify + ProxyPassReverse /notify http://127.0.0.1:8080/notify + ProxyPass /upload http://127.0.0.1:8080/upload + ProxyPassReverse /upload http://127.0.0.1:8080/upload + # + # Order deny,allow + # Allow from all + # Authtype Basic + # Authname "Password Required" + # AuthUserFile /etc/apache2/.htpasswd + # Require valid-user + # + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + +--- +API run as service +sudo nano /etc/systemd/system/fostats-api.service + +#fostats-api.service +[Unit] +Description=FO Stats Notify + +[Service] +ExecStart=/var/www/api/server.js +Restart=always +User=root +# Note Debian/Ubuntu uses 'nogroup', RHEL/Fedora uses 'nobody' +Group=nogroup +Environment=PATH=/usr/bin:/usr/local/bin +Environment=NODE_ENV=production +WorkingDirectory=/var/www/api + +[Install] +WantedBy=multi-user.target diff --git a/http/api/api.js b/http/api/api.js new file mode 100644 index 0000000..be4fa96 --- /dev/null +++ b/http/api/api.js @@ -0,0 +1,23 @@ +#!/usr/bin/env nodejs +//const cors = require("cors"); +const express = require("express"); +const app = express(); +global.__basedir = __dirname; + +/*var corsOptions = { + origin: "http://localhost:8081" +}; + +app.use(cors(corsOptions));*/ + +const initRoutes = require("./routes"); + +app.use(express.urlencoded({ extended: true })); +initRoutes(app); + +let port = 8080; +app.enable('trust proxy'); + +app.listen(port, () => { + console.log(`Running at localhost:${port}`); +}); diff --git a/http/api/include/controller.js b/http/api/include/controller.js new file mode 100644 index 0000000..3cffefa --- /dev/null +++ b/http/api/include/controller.js @@ -0,0 +1,147 @@ +const { exec } = require("child_process"); +const uploadFile = require("./upload"); +global.HttpDir = '/var/www/html/'; +global.LogDir = '/var/www/html/'; +global.UploadDir = '/var/www/html/.upload/'; + +function logRequest (req,file,param) { + var fs = require('fs'); + const d = new Date(); + fs.readFile(file,function (err,data){ + if (err) { console.log(err); } + if (data.length > 60000) { + txt2 = removeLastLine(data.toString()) + } else { txt2 = data.toString(); } + + fs.writeFile(file,d.toISOString() + '\t| ' + req.ip + '\t| ' + param + '\n' + txt2,function (err2){ + if (err2) { console.log(err2); } + }); + }); +} + +function removeLastLine (data) { + if(data.lastIndexOf("\n") > 0) { + return data.substring(0, x.lastIndexOf("\n")); + } else { + return data; + } +} + +function removeLines (data, lines = []) { + return data + .split('\n') + .filter((val, idx) => lines.indexOf(idx) === -1) + .join('\n'); +} + + +const upload = async (req, res) => { + try { + await uploadFile(req, res); + + if (req.file == undefined) { + return res.status(400).send({ message: "Please upload a file!" }); + } + + var fs = require('fs'); + if ( fs.existsSync(HttpDir + req.params.path + "/" + req.file.originalname) || + fs.existsSync(HttpDir + req.params.path + "/" + req.file.originalname.replace('[.]json$','.html')) ) { + fs.unlinkSync(UploadDir + req.file.originalname); + return res.status(400).send({ message: "File already exists: " + req.params.path + "/" + req.file.originalname, }); + } else { + fs.rename(UploadDir + req.file.originalname, HttpDir + req.params.path + "/" + req.file.originalname); + } + + logRequest(req,LogDir + '.upload2.log',req.params.path + "/" + req.file.originalname); + + res.status(200).send({ + message: "Uploaded the file successfully: " + req.params.path + "/" + req.file.originalname, + }); + + exec('pwsh ' + HttpDir + '_FoDownloader.ps1 -OutFolder ' + HttpDir + ' -LocalFile ' + req.params.path + '/' + req.file.originalname + ' -LimitDays 90 -NewOnlyBatch > ' + LogDir + '.upload.log'); + logRequest(req,LogDir + '.upload2.log',req.params.path + "/" + req.file.originalname); + } catch (err) { + res.status(500).send({ + message: `Could not upload the file: ${req.file.originalname}. ${err}`, + }); + } +}; + + +const notify = (req, res) => { + url = req.params.path; + + if ( url.match("^.+/(staging|quad|scrim|tourney)/.+[.]json$") ) { + logRequest(req,LogDir + '.notify2.log',url); + exec('pwsh ' + HttpDir + '_FoDownloader.ps1 -FilterPath ' + url + ' -LimitMins 30 -NewOnlyBatch >> ' + LogDir + '.notify.log'); + logRequest(req,LogDir + '.notify2.log',url); + res.status(200).send({ message: "Processing: " + url, }); + } else { res.status(400).send({ message: "Invalid path: " + url, }); } +}; + + +const add2v2 = (req, res) => { + url = req.params.path; + if (url.match("^.+/.+/.+\]$")) { url = url + '_blue_vs_red_stats.json'; } + if (url.match("^.+/.+/.+[.]json$")) { + var fs = require('fs'); + + var excluded = false; + fs.readFile(HttpDir + '.2v2_tourney_exclude.txt',function (err,data){ + if (err) { console.log(err); } + + urlre = url.replace(/(\[|\])/g,'\\$1'); + urlre = '(^|\n)' + urlre + '(\n|$)'; + re = new RegExp(urlre,'g'); + + if (data.toString().match(re)) { excluded = true; } + if (excluded == true) { + var txt = data.toString(); + txt = txt.replace(re,'$1'); + fs.writeFile(HttpDir + '.2v2_tourney_exclude.txt',txt,function (err2){ + if (err2) { console.log(err2); } + console.log(url + ' has been removed from ' + HttpDir + '.2v2_tourney_exclude.txt'); + }); + } + }); + + exec('pwsh ' + HttpDir + 'FO_stats_join-json.ps1 -FilterPath ' + HttpDir + url + ' -StartOffsetDays 365 -PlayerCount \'^4$\' -OutFile ' + HttpDir + '2v2_tourney_stats.json'); + res.status(200).send({ message: "Adding " + url }); + } else { res.status(400).send({ message: "Invalid path: " + url }); } +} + + +const rem2v2 = (req, res) => { + url = req.params.path; + + if (url.match("^.+/.+/.+\]$")) { url = url + '_blue_vs_red_stats.json'; } + + if (url.match("^.+/.+/.+[.]json$")) { + var fs = require('fs'); + var excluded = false; + fs.readFile(HttpDir + '.2v2_tourney_exclude.txt',function (err,data){ + if (err) { console.log(err); } + urlre = url.replace(/(\[|\])/g,'\\$1'); + urlre = '(^|\n)' + urlre + '(\n|$)'; + re = new RegExp(urlre,'g'); + if (data.toString().match(re)) { excluded = true; } + if (excluded == false) { + fs.writeFile(HttpDir + '.2v2_tourney_exclude.txt',data.toString() + '\n' + url,function (err2){ + if (err2) { console.log(err2); } + console.log(url + ' has been added to ' + HttpDir + '.2v2_tourney_exclude.txt'); + }); + } + }); + + exec('pwsh ' + HttpDir + 'FO_stats_join-json.ps1 -RemoveMatch ' + HttpDir + url + ' -FromJson ' + HttpDir + '2v2_tourney_stats.json > ' + LogDir + '.2v2-remove.log'); + res.status(200).send({ message: "Removing " + url}); + } else { res.status(400).send({ message: "Invalid path: " + url }); } +} + + +module.exports = { + upload, + notify, + add2v2, + rem2v2 +}; diff --git a/http/api/include/upload.js b/http/api/include/upload.js new file mode 100644 index 0000000..77be531 --- /dev/null +++ b/http/api/include/upload.js @@ -0,0 +1,21 @@ +const util = require("util"); +const multer = require("multer"); +const maxSize = 5 * 1024 * 1024; + +let storage = multer.diskStorage({ + destination: (req, file, cb) => { + cb(null, UploadDir); + }, + filename: (req, file, cb) => { + console.log(file.originalname); + cb(null, file.originalname); + }, +}); + +let uploadFile = multer({ + storage: storage, + limits: { fileSize: maxSize }, +}).single("file"); + +let uploadFileMiddleware = util.promisify(uploadFile); +module.exports = uploadFileMiddleware; diff --git a/http/api/routes/index.js b/http/api/routes/index.js new file mode 100644 index 0000000..cc5fbc1 --- /dev/null +++ b/http/api/routes/index.js @@ -0,0 +1,17 @@ +#!/usr/bin/env nodejs +const express = require("express"); +const router = express.Router(); +const controller = require("../include/controller"); + +let routes = (app) => { + router.post("/upload/:path([a-z0-9]+/[a-z0-9]+)", controller.upload); + router.post("/notify/:path(*[.]json)$", controller.notify); + router.get("/notify/:path(*[.]json)$", controller.notify); + router.get("/2v2-add/:path(*[.]json)$", controller.add2v2); + router.get("/2v2-remove/:path(*[.]json)$", controller.rem2v2); + router.get("/2v2-add/:path(*%5D)$", controller.add2v2); + router.get("/2v2-remove/:path(*%5D)$", controller.rem2v2); + app.use(router); +}; + +module.exports = routes; diff --git a/http/fo_daily.css b/http/fo_daily.css new file mode 100644 index 0000000..5942c82 --- /dev/null +++ b/http/fo_daily.css @@ -0,0 +1,36 @@ +body { + font-family: Verdana, Arial, Geneva, Helvetica, sans-serif; + font-size: 12px; + color: black; +} +table, td, th { + border-color: black; + border-style: solid; + font-family: Verdana, Arial, Geneva, Helvetica, sans-serif; + font-size: 11px; +} +table { + border-width: 0 0 1px 1px; + border-spacing: 0; + border-collapse: collapse; +} +tr:nth-child(odd){ + background-color: lightgrey; +} +td, th { + margin: 0; + padding: 4px; + border-width: 1px 1px 0 0; + text-align: left; +} +th { + color: white; + background-color: black; + font-weight: bold; +} +div { + padding-top: 1px; + padding-right: 5px; + padding-bottom: 1x; + padding-left: 1px; +} \ No newline at end of file diff --git a/http/fo_daily.js b/http/fo_daily.js new file mode 100644 index 0000000..8a075a8 --- /dev/null +++ b/http/fo_daily.js @@ -0,0 +1,40 @@ +function linkify(inputText) { + var replacedText, replacePattern1, replacePattern2, replacePattern3; + replacePattern1 = /((([-A-z0-9]+\/){2})([A-Za-z0-9-_\[\]]+))/gim; + replacedText = inputText.replace(replacePattern1, '$2$4'); + replacedText = replacedText.replace(/Match<\/th>/gim, 'ServerMatch'); + return replacedText; +} + +function linkify2(tbody) { + for (var i = 0, row; row = tbody.rows[i]; i++) { + row.cells[0].innerHTML = '' + row.cells[0].innerText + ''; + row.cells[1].innerHTML = '' + row.cells[1].innerHTML + ''; + } + return tbody.innerHTML; +} + + +function fo_daily_post () { + var FOJoinJsonVersion = document.getElementById('FOJoinJsonVersion'); + if (!FOJoinJsonVersion) { FOJoinJsonVersion = 1.0; } + else { FOJoinJsonVersion = Number(FOJoinJsonVersion.content);} + + if (FOJoinJsonVersion > 1.0) { + var table = document.getElementById('MatchLog'); + if (table.getElementsByTagName('thead')[0].rows[0].cells[0].innerText == 'Server') { + var tbody = table.getElementsByTagName('tbody')[0]; + tbody.innerHTML = linkify2(tbody); + } + } else { + document.body.innerHTML = linkify(document.body.innerHTML); + } + + new Tablesort(document.getElementById('MatchLog'), { descending: true }); + new Tablesort(document.getElementById('AttackSummary'), { descending: true }); + new Tablesort(document.getElementById('DefenceSummary'), { descending: true }); + new Tablesort(document.getElementById('ClassKillsAttack'), { descending: true }); + new Tablesort(document.getElementById('ClassKillsDefence'), { descending: true }); + new Tablesort(document.getElementById('ClassTimeAttack'), { descending: true }); + new Tablesort(document.getElementById('ClassTimeDefence'), { descending: true }); +} \ No newline at end of file diff --git a/http/fo_stats.css b/http/fo_stats.css new file mode 100644 index 0000000..e3e2b8d --- /dev/null +++ b/http/fo_stats.css @@ -0,0 +1,228 @@ +/*html { display: table; margin: auto; }*/ + +html,body,table { font-size: 105%; } + +h1 { margin-bottom: 1; } + +a:link { color: lightyellow;} +a:visited { color: lightyellow; } +a:hover { color: orange; } + +body { +/* display: table-cell;vertical-align: middle;*/ + font-family: "Calibri"; color:white; background-color:rgb(56, 75, 94); +} + +.hideTable, .hideTable tr, .hideTable td { +color: black; +background-color: transparent; +border: 0; +padding: 0; +padding-left: 5px; +} + +h1 { color:aliceblue; } +h2 { color:#fff3cb; margin-top:20px; margin-bottom: 10px } +h3, h4 { margin-top:5px; margin-bottom: 5px; } +h4 { font-weight: normal } + +th { background-color: rgb(19, 37, 56);} +tr { background-color: rgb(34, 58, 85);} +/*tr:nth-child(odd){ background-color: rgb(51, 79, 110); }*/ + + +table, th, td { + white-space: nowrap; + padding: 3px; + border: 1px solid rgb(122, 122, 122); + border-collapse: collapse; + min-width: 20px; +} + +.max { + font-weight: 800; + color: #ffd700; +} + +.rowTeam1 { background-color: #2357b9 } +.rowTeam1:nth-child(odd){ background-color: #173772 ; } +.rowTeam2 { background-color: #9e2222; } +.rowTeam2:nth-child(odd) { background-color: #641414; } +.rowTeamBoth { background-color: #b71dbd } +.cellGrey { background-color:rgb(45, 62, 80) } +.cellGrey:nth-child(odd) { background-color:rgb(33, 48, 65) } +.cellAmber { color:black; background-color: #fcd600 } +/*.cellAmber:nth-child(odd) { color:black; background-color: #ffc400 }*/ +.cellOrange { background-color: #c74e16 } +.cellOrange:nth-child(odd) { background-color: #ad4312 } +.cellGreen { background-color: #2b8b24 } +.cellGreen:nth-child(odd) { background-color: #2f6b2b } + +th[role=columnheader]:not(.no-sort) { + cursor: pointer; +} + + + +.VersusHover { + position: relative; + display: inline-block; + cursor: pointer; +} + +.VersusHover .VersusHoverTextGreen, .VersusHover .VersusHoverTextAmber, .VersusHover .VersusHoverTextOrange { + font-size: 85%; + font-weight: normal; + visibility: hidden; + width: auto; + /*background-color: lightblue;*/ + color: black; + text-align: left; + border-radius: 6px; + padding: 5px 0; + padding-left: 10px; + padding-right: 10px; + position: absolute; + z-index: 1; + bottom: 130%; + left: 44px; + margin-left: -60px; +} + + +.VersusHover .VersusHoverTextGreen::after, .VersusHoverTextAmber::After, .VersusHoverTextOrange::After { + content: ""; + position: absolute; + top: 100%; + left: 20px; + margin-left: -5px; + border-width: 7px; + border-style: solid; + /*border-color: lightblue transparent transparent transparent;*/ +} + + +.VersusHover .VersusHoverTextHeader { + font-size: 85%; + visibility: hidden; + width: auto; + color: black; + text-align: left; + border-radius: 6px; + padding: 5px 0; + padding-left: 10px; + padding-right: 15px; + position: absolute; + z-index: 1; + bottom: 130%; + left: 44px; + margin-left: -60px; + background-color: whitesmoke; +} + +.VersusHover .VersusHoverTextHeader::after { + content: ""; + position: absolute; + top: 100%; + left: 19px; + margin-left: -5px; + border-width: 7px; + border-style: solid; + border-color: whitesmoke transparent transparent transparent; +} + + + +.VersusHover .VersusHoverTextGreen::after { border-color: lightgreen transparent transparent transparent } +.VersusHover .VersusHoverTextGreen { background-color: lightgreen } + +.VersusHover .VersusHoverTextGreen::after { border-color: lightgreen transparent transparent transparent } +.VersusHover .VersusHoverTextGreen { background-color: lightgreen } +.VersusHover .VersusHoverTextAmber::after { border-color: lightyellow transparent transparent transparent } +.VersusHover .VersusHoverTextAmber { background-color: lightyellow } +.VersusHover .VersusHoverTextOrange::after { border-color: #FFC2AA transparent transparent transparent } +.VersusHover .VersusHoverTextOrange { background-color: #FFC2AA } + + +.VersusHover:hover .VersusHoverTextGreen, .VersusHover:hover .VersusHoverTextAmber, .VersusHover:hover .VersusHoverTextOrange, +.VersusHover:hover .VersusHoverTextHeader { + visibility: visible; +} + + +.ClassHover, .PlayerHover { + position: relative; + display: inline-block; + cursor: pointer; +} + +.ClassHover .ClassHoverText1, .ClassHover .ClassHoverText2, .ClassHover .ClassHoverText0 { + font-weight: normal; + font-size: 85%; + visibility: hidden; + width: auto; + color: black; + text-align: left; + border-radius: 6px; + padding: 5px 0; + padding-left: 10px; + padding-right: 15px; + position: absolute; + z-index: 1; + bottom: 130%; + left: 55; + margin-left: -60px; +} + +.ClassHover .ClassHoverText1::after, .ClassHoverText2::After, .ClassHoverText0::After { + content: ""; + position: absolute; + top: 100%; + left: 15%; + margin-left: -5px; + border-width: 7px; + border-style: solid; +} + +.ClassHover .ClassHoverText1::after, .PlayerHover .PlayerHoverText1::after { border-color: lightblue transparent transparent transparent } +.ClassHover .ClassHoverText1, .PlayerHover .PlayerHoverText1 { background-color: lightblue } +.ClassHover .ClassHoverText2::after, .PlayerHover .PlayerHoverText2::after { border-color: #ffb5ad transparent transparent transparent } +.ClassHover .ClassHoverText2, .PlayerHover .PlayerHoverText2 { background-color: #ffb5ad } +.ClassHover .ClassHoverText1::after { border-color: lightblue transparent transparent transparent } +.ClassHover .ClassHoverText1 { background-color: lightblue } +.ClassHover .ClassHoverText0::after, .PlayerHover .PlayerHoverText0::after { border-color: violet transparent transparent transparent } +.ClassHover .ClassHoverText0, .PlayerHover .PlayerHoverText0 { background-color: violet } + + +.ClassHover:hover .ClassHoverText1, .ClassHover:hover .ClassHoverText2, .ClassHover:hover .ClassHoverText0, +.PlayerHover:hover .PlayerHoverText1, .PlayerHover:hover .PlayerHoverText2, .PlayerHover:hover .PlayerHoverText0 { + visibility: visible; +} + + +.PlayerHover .PlayerHoverText1, .PlayerHover .PlayerHoverText2, .PlayerHover .PlayerHoverText0 { + font-size: 80%; + visibility: hidden; + width: auto; + color: black; + text-align: left; + border-radius: 6px; + padding: 5px 0; + padding-left: 10px; + padding-right: 15px; + position: absolute; + z-index: 1; + bottom: 130%; + left: 55; + margin-left: -60px; +} + +.PlayerHover .PlayerHoverText1::after, .PlayerHoverText2::After, .PlayerHoverText0::After { + content: ""; + position: absolute; + top: 100%; + left: 15px; + margin-left: -5px; + border-width: 7px; + border-style: solid; +} diff --git a/http/fo_stats.js b/http/fo_stats.js new file mode 100644 index 0000000..174361a --- /dev/null +++ b/http/fo_stats.js @@ -0,0 +1,527 @@ +function ConvertTimeToMins(strTime) { + var minsec = strTime.split(':'); + + if (minsec.length != 2) { return; } + return Number(minsec[0]) + (Number(minsec[1]) / 60) +} + +function MakeVersusHover(strTable) { + var table = document.getElementById(strTable); + for (var j = 0, col; col = table.rows[0].cells[j]; j++) { + let val = col.innerText; + if (val === "1") { var pvpStart = j; break; } + } + + for (var i = 1, row; row = table.rows[i]; i++) { + if (table.rows[i].cells[0].innerText.match("^Total:.*$")) { break; } + var id = row.cells[0].innerText; + var div = document.createElement('div'); + + if (table.rows[0].cells[pvpStart + i - 1].innerText != "Classes") { + div.innerHTML = id + '' + row.cells[1].innerText + ''; + div.className = 'VersusHover'; + table.rows[0].cells[pvpStart + i - 1].innerHTML = div.outerHTML; + } + + for (var j = pvpStart, col; col = table.rows[i].cells[j]; j++) { + if (table.rows[0].cells[j].innerText === "Classes") { + GetClassTime(strTable, id, col); + break; + } + + let pos = table.rows[0].cells[j].innerText; + var opTeam = table.rows[pos].cells[2].innerText; + var opValue = table.rows[pos].cells[pvpStart + i - 1].innerText; + + if (pos == i) { var hoverClass = "Amber"; var hoverTitle = "Self-affliction" } + else if (row.cells[2].innerText == opTeam) { var hoverClass = "Orange"; var hoverTitle = "Friendly fire"; } + else { var hoverClass = "Green"; var hoverTitle = "Head to Head"; } + + var div = document.createElement('div'); + div.innerHTML = col.innerText + '' + hoverTitle + '' + + '
' + + row.cells[1].innerText + ":" + col.innerText + "
" + + table.rows[pos].cells[1].innerText + ': ' + opValue + '
'; + col.id = strTable + '-' + i + '-' + pos; + div.className = 'VersusHover'; + + col.innerHTML = div.outerHTML; + } + } + + HighlightMaxVersus(strTable); + +} + +function MakeSummaryHover(strTable) { + var table = document.getElementById(strTable); + for (var j = 0, col; col = table.rows[0].cells[j]; j++) { + if (col.innerText === "Classes") { var classStart = j; break; } + } + + for (var i = 1, row; row = table.rows[i]; i++) { + if (row.cells[0].innerText.match("^Total:.*$")) { break; } + GetClassTime(strTable, row.cells[0].innerText, row.cells[classStart]); + } +} + +function MakePerMinFragHover(strTable) { + var table = document.getElementById(strTable).getElementsByTagName('tbody')[0]; + var header = document.getElementById(strTable).getElementsByTagName('thead')[0]; + + if (strTable.match("^class.*$")) { var start = 4; } + else { var start = 6; } + + for (var i = 0, row; row = table.rows[i]; i++) { + var round = 1; + for (var j = start, col; col = row.cells[j]; j++) { + var txt = ''; + var strKD = col.innerText; + if (strKD) { + var arrKD = strKD.split('/'); + if (arrKD.length == 1 || arrKD[1] == 0) { + var kd = "∞"; + } else { + var kd = arrKD[0] / arrKD[1]; + kd = Number.parseFloat(kd).toFixed(2); + } + var rank = arrKD[0] - arrKD[1]; + var team = row.cells[2].innerText; + if (team.match("^.&.$") || team == '') { team = '0'; } + + txt = strKD + + '' + + '' + + ''; + + if (header.rows[0].cells[1].innerText.match("^(Rnd|Round)[1-2]") && + header.rows[1].cells[j].innerText.match("^Sco|Sold|Demo|Med|HwG|Pyro|Spy|Eng$")) { + var startRnd2 = 4 + header.rows[0].cells[1].colSpan; + if (j < startRnd2) { round = 1; } + else { round = 2; } + var time = GetClassTimeValue(i, round, header.rows[1].cells[j].innerText); + + txt = txt + + ''; + } + + txt = txt + '
Rank:' + rank + '
K/D:' + kd + '
KPM:' + Number.parseFloat((arrKD[0] / ConvertTimeToMins(time))).toFixed(2) + + '
Time:' + time + + '
'; + + var div = document.createElement('div'); + div.innerHTML = txt; + div.className = "ClassHover"; + col.innerHTML = div.outerHTML; + } + } + } +} + +function GetClassTimeValue(id, round, playerclass) { + var cTable = document.getElementById("classTime"); + var start = 4 + ((Number(round) - 1) * 10); + + for (var i = 2, row; row = cTable.rows[i]; i++) { + if (row.cells[0].innerText == Number(id) + 1) { + for (var j = start, col; col = cTable.rows[i].cells[j]; j++) { + var tfClass = cTable.rows[1].cells[j].innerText; + if (tfClass == 'K/D') { break; } + if (tfClass == playerclass) { return col.innerText; } + } + } + } + return '' +} + +function GetClassTime(strTable, id, classCol) { + var cTable = document.getElementById("classTime"); + var team = cTable.rows[Number(id) + 1].cells[2].innerText; + + if ((strTable.match("^.*Attack$") && team.match("^1.*$")) || (strTable.match("^.*Defence$") && team.match("^2.*$"))) { + var round = 1; + } else if ((strTable.match("^.*Attack$") && team.match("^2.*$")) || (strTable.match("^.*Defence$") && team.match("^1.*$"))) { + var round = 2; + } else { + var round = strTable.slice(-1); + } + + var start = 4 + ((round - 1) * 10); + if (round == 2) { start = start - 1; } + let hoverText = ""; + let cStr = classCol.innerText; + + for (var i = 2, row; row = cTable.rows[i]; i++) { + if (row.cells[0].innerText == id) { + for (var j = start, col; col = cTable.rows[i].cells[j]; j++) { + var tfClass = cTable.rows[1].cells[j].innerText; + if (tfClass == 'K/D') { break; } + if (col.innerText == "") { continue; } + if (hoverText != "") { hoverText = hoverText + "
"; } + hoverText = hoverText + "" + tfClass + ": " + col.innerText; + } + + + if (cTable.rows[i].cells[2].innerText.match("^(1|2)$")) { + var hoverClass = cTable.rows[i].cells[2].innerText; + } else { + var hoverClass = "0"; + } + + var div = document.createElement('div'); + div.innerHTML = cStr + '' + hoverText + ''; + div.className = 'ClassHover'; + classCol.innerHTML = div.outerHTML; + } + + } +} + +function MakePlayerProfiles(strTable, kind) { + var table = document.getElementById(strTable); + var flagTable = document.getElementById("perMinFlag").getElementsByTagName('tbody')[0]; + + for (var i = 0, row; row = table.getElementsByTagName('tbody')[0].rows[i]; i++) { + let player = row.cells[0].innerText; + let team = row.cells[2].innerText; + if (team == '' || team == 'None') { continue; } + + var plusRound2 = 0; + if (kind == 'both') { + var round = 1; + var plusRound2 = 2; + } else if (kind == 'round') { + var round = strTable.slice(-1); + } else if ((kind == 'attack' && team.match("^1.*$")) || (kind == 'defence' && team.match("^2.*$"))) { + var round = 1; + } else if ((kind == 'attack' && team.match("^.*2$")) || (kind == 'defence' && team.match("^.*1$"))) { + var round = 2; + } + + var fragTable = document.getElementById("fragRound" + round).getElementsByTagName('tbody')[0]; + var damageTable = document.getElementById("damageRound" + round).getElementsByTagName('tbody')[0]; + var classTitle = "Classes:"; + + for (var j = 0, row2; row2 = fragTable.rows[j]; j++) { + + if (row2.cells[0].innerText == player) { + frags = Number(row2.cells[3].innerText); + deaths = Number(row2.cells[4].innerText); + tkills = Number(row2.cells[5].innerText); + dmg = Number(damageTable.rows[j].cells[3].innerText); + caps = Number(flagTable.rows[j].cells[3].innerText); + stops = Number(flagTable.rows[j].cells[7].innerText); + ftime = flagTable.rows[j].cells[6].innerText + takes = Number(flagTable.rows[j].cells[4].innerText); + cls = fragTable.rows[j].cells[fragTable.rows[j].cells.length - 1].innerText; + break; + } + } + + if (plusRound2 > 0) { + var fragTable = document.getElementById("fragRound2").getElementsByTagName('tbody')[0]; + var damageTable = document.getElementById("damageRound2").getElementsByTagName('tbody')[0]; + + for (var j = 0, row2; row2 = fragTable.rows[j]; j++) { + if (row2.cells[0].innerText == player) { + frags = frags + Number(row2.cells[3].innerText); + deaths = deaths + Number(row2.cells[4].innerText); + tkills = tkills + Number(row2.cells[5].innerText); + dmg = dmg + Number(damageTable.rows[j].cells[3].innerText); + cls = cls + '
' + fragTable.rows[j].cells[fragTable.rows[j].cells.length - 1].innerText; + classTitle = "Classes Rnd1:
Classes Rnd2:" + break; + } + } + } + + + var div = document.createElement('div'); + var cStr = row.cells[1].innerText; + + if (kind == 'both') { + hoverTitle = 'Attack & Defence' + } else if (kind == 'attack' || (round == 1 && team.match("^1.*$")) || (round == 2 && team.match("^.*2$"))) { + hoverTitle = 'Attack' + } else if (kind == 'defence' || (round == 1 && team.match("^2.*$")) || (round == 2 && team.match("^.*1$"))) { + hoverTitle = 'Defence' + } + + if (row2.cells[2].innerText.match("^(1|2)$")) { + var hoverClass = row2.cells[2].innerText; + } else { + var hoverClass = "0"; + } + + var divTxt = cStr + ''; + if (kind == 'attack' || kind == 'both' || hoverTitle == 'Attack') { + divTxt = divTxt + ''; + divTxt = divTxt + ''; + } + if (kind == 'defence' || kind == 'both' || hoverTitle == 'Defence') { + divTxt = divTxt + ''; + } + divTxt = divTxt + + '' + + '' + + '' + + '' + + ''; + + div.innerHTML = divTxt; + div.className = 'PlayerHover'; + row.cells[1].innerHTML = div.outerHTML; + } +} + +function MakeAwardPlayerProfile(strTable) { + var table = document.getElementById(strTable); + if (strTable == "awardDefence") { + var copyTable = document.getElementById("summaryDefence").getElementsByTagName('tbody')[0]; + } else { + var copyTable = document.getElementById("summaryAttack").getElementsByTagName('tbody')[0]; + } + + for (var i = 0, row; row = table.getElementsByTagName('tbody')[0].rows[i]; i++) { + if (row.cells.length < 3) { continue; } + let names = row.cells[1].innerText.split(','); + let newInnerTxt = ""; + for (var j = 0, player; player = names[j]; j++) { + for (var k = 0, row2; row2 = copyTable.rows[k].cells[1]; k++) { + if (row2 == "") { continue; } + if (row2.innerText == player.replace('*', '').trim()) { + if (newInnerTxt != "") { newInnerTxt = newInnerTxt + ", " } + var span = row2.getElementsByTagName('span')[0]; + var div = document.createElement('div'); + div.className = 'PlayerHover'; + div.innerHTML = player + span.outerHTML; + newInnerTxt = newInnerTxt + div.outerHTML; + break; + } + } + } + + if (newInnerTxt != "") { row.cells[1].innerHTML = newInnerTxt; } + } +} + +function MakeClassTimeHover(strTable) { + + var table = document.getElementById(strTable).getElementsByTagName('tbody')[0]; + var header = document.getElementById(strTable).getElementsByTagName('thead')[0]; + var ckTable = document.getElementById('classKills').getElementsByTagName('tbody')[0]; + + var start = 4; + + for (var i = 0, row; row = table.rows[i]; i++) { + for (var j = start, col; col = row.cells[j]; j++) { + if (col.innerText == '' || header.rows[1].cells[j].innerText == 'K/D') { continue; } + + var txt = ''; + var ckValue = ckTable.rows[i].cells[j].innerText; + + if (ckValue != '') { + strKD = ckValue.split('/'); + } else { continue; } + var kills = Number(strKD[0]); + var dth = Number(strKD[1]); + + if (strKD.length == 1 || dth == 0) { + var kd = "∞"; + } else { + var kd = kills / dth; + kd = Number.parseFloat(kd).toFixed(2); + } + + var rank = kills - dth; + var team = row.cells[2].innerText; + if (team.match("^.&.$") || team == '') { team = '0'; } + var minsec = col.innerText.split(':'); + var mins = Number(minsec[0]) + (Number(minsec[1]) / 60); + + txt = col.innerText + + '' + + '
' + hoverTitle + '
Caps/Takes:' + caps + ' / ' + takes + '
Flag Time:' + ftime + '
Flag stops:' + stops + '
Kill/Death:' + frags + ' / ' + deaths + '
Damage:' + dmg + '
TKill:' + tkills + '
' + classTitle + '' + cls + '
' + + '' + + /*'Death: ' + dth + '
' +*/ + '' + + '
Kill/Dth:' + kills + ' / ' + dth + '
KPM:' + Number.parseFloat(kills / mins).toFixed(2) + '
Rank:' + rank + '
K/D:' + kd + '
'; + + var div = document.createElement('div'); + div.innerHTML = txt; + div.className = "ClassHover"; + col.innerHTML = div.outerHTML; + } + } +} + +function MakeH4PlayerProfile() { + var elements = document.getElementsByTagName('h4'); + var copyTable = document.getElementById("perMinFragDeath").getElementsByTagName('tbody')[0]; + + for (var i = 0, name; name = elements[i].innerText; i++) { + let newInnerTxt = ""; + for (var k = 0, row2; row2 = copyTable.rows[k].cells[1]; k++) { + if (row2.innerText == "") { continue; } + if (row2.innerText == name) { + if (newInnerTxt != "") { newInnerTxt = newInnerTxt + ", " } + var span = row2.getElementsByTagName('span')[0]; + var div = document.createElement('div'); + div.className = 'PlayerHover'; + div.innerHTML = name + span.outerHTML; + newInnerTxt = newInnerTxt + div.outerHTML; + break; + } + } + + if (newInnerTxt != "") { elements[i].innerHTML = newInnerTxt; } + if (!elements[i+1]) { break; } + } +} + +function InsertDailyStatsURL() { + var folders = window.location.pathname.split('/'); + var location = '/' + folders[folders.length - 3] + '/' + folders[folders.length - 2]; + let fopath = window.location.pathname; + fopath = fopath.substring(1).replace('.html', ''); + + var txt = '
Server : ' + '' + location + ' | '; + txt = txt + 'Daily stats : North-America / ' + txt = txt + 'Oceania / ' + txt = txt + 'Europe / ' + txt = txt + 'Brasil / ' + txt = txt + 'International | ' + txt = txt + 'Demo : MVD | ' + txt = txt + 'Archive : JSON' + txt = txt + '
' + var anchor = document.getElementsByTagName('h1')[0].insertAdjacentHTML('afterEnd', txt); +} + +function HighlightMaxVersus(strTable) { + var table = document.getElementById(strTable).getElementsByTagName('tbody'); + var elements = table[0].getElementsByClassName("cellGreen"); + const values = []; + let ids = []; + for (i = 0; i < elements.length; i++) { + if (strTable == "perMinFragDeath") { + if (!elements[i].innerText.match("^[0-9]+\/[0-9]+")) { continue; } + split = elements[i].innerText.split('/'); + values.push(Number(split[0] - split[1])); + } else if (elements[i].innerText.match("^[0-9]+$")) { + values.push(Number(elements[i].innerText)); + } else { continue; } + ids.push(elements[i].id); + } + max = Math.max(...values); + + if (ids[0]) { + for (i = 0; i < values.length; i++) { + if (values[i] == max) { + document.getElementById(ids[i]).classList.add("max"); + } + } + } else { + for (i = 0; i < elements.length; i++) { + if (values[i] == max) { + elements[i].classList.add("max"); + } + } + } + +} + +function HighlightMax(strTable, column) { + table = document.getElementById(strTable).getElementsByTagName('tbody')[0]; + elements = table.querySelectorAll('td:nth-child(' + column + ')'); + const values = []; + + for (i = 0; i < elements.length; i++) { + if (elements[i].innerText.match("^[0-9]+(\.[0-9]+)?$")) { + values.push(Number(elements[i].innerText.replace(':', ''))); + } + } + max = Math.max(...values); + for (i = 0; i < elements.length; i++) { + if (elements[i].innerText.replace(':', '') == max) { + elements[i].classList.add("max"); + } + } +} + + + +function FO_Post() { + var FOStatsVersion = document.getElementById('FOStatsVersion'); + if (!FOStatsVersion) { FOStatsVersion = 2.0; } + else { FOStatsVersion = Number(FOStatsVersion.content);} + + new Tablesort(document.getElementById('summaryAttack'), { descending: true }); + new Tablesort(document.getElementById('summaryDefence'), { descending: true }); + new Tablesort(document.getElementById('fragRound1'), { descending: true }); + new Tablesort(document.getElementById('fragRound2'), { descending: true }); + new Tablesort(document.getElementById('damageRound1'), { descending: true }); + new Tablesort(document.getElementById('damageRound2'), { descending: true }); + new Tablesort(document.getElementById('perMinFragDeath'), { descending: true }); + new Tablesort(document.getElementById('perMinDamage'), { descending: true }); + new Tablesort(document.getElementById('perMinFlag'), { descending: true }); + new Tablesort(document.getElementById('classKills'), { descending: true }); + new Tablesort(document.getElementById('classTime'), { descending: true }); + InsertDailyStatsURL(); + MakeVersusHover("fragRound1"); + MakeVersusHover("fragRound2"); + MakeVersusHover("damageRound1"); + MakeVersusHover("damageRound2"); + MakeSummaryHover("summaryAttack"); + MakeSummaryHover("summaryDefence"); + MakePlayerProfiles("summaryAttack", 'attack'); + MakePlayerProfiles("summaryDefence", 'defence'); + MakePlayerProfiles("fragRound1", 'round'); + MakePlayerProfiles("fragRound2", 'round'); + MakePlayerProfiles("damageRound1", 'round'); + MakePlayerProfiles("damageRound2", 'round'); + MakePlayerProfiles("perMinFragDeath", 'both'); + MakePlayerProfiles("perMinDamage", 'both'); + MakePlayerProfiles("perMinFlag", 'both'); + MakePlayerProfiles("classKills", 'both'); + MakePlayerProfiles("classTime", 'both'); + MakeAwardPlayerProfile("awardDefence"); + MakeAwardPlayerProfile("awardAttack"); + MakePerMinFragHover("perMinFragDeath"); + MakePerMinFragHover("classKills"); + HighlightMax("summaryAttack", '4'); + HighlightMax("summaryAttack", '5'); + HighlightMax("summaryAttack", '6'); + HighlightMax("summaryAttack", '7'); + HighlightMax("summaryAttack", '9'); + HighlightMax("summaryAttack", '10'); + if (FOStatsVersion >= 2.11) { + HighlightMax("summaryAttack", '11'); + HighlightMax("summaryAttack", '12'); + HighlightMax("summaryAttack", '14'); + } else { + HighlightMax("summaryAttack", '11'); + HighlightMax("summaryAttack", '13'); + } + HighlightMax("summaryDefence", '4'); + HighlightMax("summaryDefence", '5'); + HighlightMax("summaryDefence", '6'); + HighlightMax("summaryDefence", '7'); + HighlightMax("summaryDefence", '9'); + HighlightMax("summaryDefence", '10'); + if (FOStatsVersion >= 2.11) { + HighlightMax("summaryDefence", '12'); + } else { + HighlightMax("summaryDefence", '11'); + } + HighlightMaxVersus("perMinDamage"); + HighlightMaxVersus("perMinFragDeath"); + HighlightMax("perMinFlag", '4'); + HighlightMax("perMinFlag", '5'); + HighlightMax("perMinFlag", '6'); + HighlightMax("perMinFlag", '7'); + HighlightMax("perMinFlag", '8'); + if (FOStatsVersion >= 2.1) { + MakeClassTimeHover('classTime'); + } + MakeH4PlayerProfile(); +} \ No newline at end of file diff --git a/http/js-tablesorts.zip b/http/js-tablesorts.zip new file mode 100644 index 0000000..6382964 Binary files /dev/null and b/http/js-tablesorts.zip differ diff --git a/http/sortable-table.css b/http/sortable-table.css new file mode 100644 index 0000000..c00d6bf --- /dev/null +++ b/http/sortable-table.css @@ -0,0 +1,101 @@ +.sr-only { + position: absolute; + top: -30em; +} + +table.sortable td, +table.sortable th { + padding: 0.125em 0.25em; + width: 8em; +} + +table.sortable th { + font-weight: bold; + border-bottom: thin solid #888; + position: relative; +} + +table.sortable th.no-sort { + padding-top: 0.35em; +} + +table.sortable th:nth-child(5) { + width: 10em; +} + +table.sortable th button { + position: absolute; + padding: 4px; + margin: 1px; + font-size: 100%; + font-weight: bold; + background: transparent; + border: none; + display: inline; + right: 0; + left: 0; + top: 0; + bottom: 0; + width: 100%; + text-align: left; + outline: none; + cursor: pointer; +} + +table.sortable th button span { + position: absolute; + right: 4px; +} + +table.sortable th[aria-sort="descending"] span::after { + content: "▼"; + color: currentcolor; + font-size: 100%; + top: 0; +} + +table.sortable th[aria-sort="ascending"] span::after { + content: "▲"; + color: currentcolor; + font-size: 100%; + top: 0; +} + +table.show-unsorted-icon th:not([aria-sort]) button span::after { + content: "♢"; + color: currentcolor; + font-size: 100%; + position: relative; + top: -3px; + left: -4px; +} + +table.sortable td.num { + text-align: right; +} + +table.sortable tbody tr:nth-child(odd) { + background-color: #ddd; +} + +/* Focus and hover styling */ + +table.sortable th button:focus, +table.sortable th button:hover { + padding: 2px; + border: 2px solid currentcolor; + background-color: #e5f4ff; +} + +table.sortable th button:focus span, +table.sortable th button:hover span { + right: 2px; +} + +table.sortable th:not([aria-sort]) button:focus span::after, +table.sortable th:not([aria-sort]) button:hover span::after { + content: "▼"; + color: currentcolor; + font-size: 100%; + top: 0; +} diff --git a/http/sortable-table.js b/http/sortable-table.js new file mode 100644 index 0000000..b1587a2 Binary files /dev/null and b/http/sortable-table.js differ diff --git a/http/tablesort.min.js b/http/tablesort.min.js new file mode 100644 index 0000000..3cf001c --- /dev/null +++ b/http/tablesort.min.js @@ -0,0 +1,282 @@ +;(function() { + function Tablesort(el, options) { + if (!(this instanceof Tablesort)) return new Tablesort(el, options); + + if (!el || el.tagName !== 'TABLE') { + throw new Error('Element must be a table'); + } + this.init(el, options || {}); + } + + var sortOptions = []; + + var createEvent = function(name) { + var evt; + + if (!window.CustomEvent || typeof window.CustomEvent !== 'function') { + evt = document.createEvent('CustomEvent'); + evt.initCustomEvent(name, false, false, undefined); + } else { + evt = new CustomEvent(name); + } + + return evt; + }; + + var getInnerText = function(el,options) { + let txt = el.getAttribute(options.sortAttribute || 'data-sort') || el.innerHTML || ''; + txt = txt.replace(//,''); + txt = txt.replace(/([0-9]+)(:)([0-9]+)/, "$1.$3"); + txt = txt.replace(/ 0) { + if (el.tHead && el.tHead.rows.length > 0) { + for (i = 0; i < el.tHead.rows.length; i++) { + if (el.tHead.rows[i].getAttribute('data-sort-method') === 'thead') { + firstRow = el.tHead.rows[i]; + break; + } + } + if (!firstRow) { + firstRow = el.tHead.rows[el.tHead.rows.length - 1]; + } + that.thead = true; + } else { + firstRow = el.rows[0]; + } + } + + if (!firstRow) return; + + var onClick = function() { + if (that.current && that.current !== this) { + that.current.removeAttribute('aria-sort'); + } + + that.current = this; + that.sortTable(this); + }; + + // Assume first row is the header and attach a click handler to each. + for (i = 0; i < firstRow.cells.length; i++) { + cell = firstRow.cells[i]; + cell.setAttribute('role','columnheader'); + if (cell.getAttribute('data-sort-method') !== 'none') { + cell.tabindex = 0; + cell.addEventListener('click', onClick, false); + + if (cell.getAttribute('data-sort-default') !== null) { + defaultSort = cell; + } + } + } + + if (defaultSort) { + that.current = defaultSort; + that.sortTable(defaultSort); + } + }, + + sortTable: function(header, update) { + var that = this, + columnKey = header.getAttribute('data-sort-column-key'), + column = header.cellIndex, + sortFunction = caseInsensitiveSort, + item = '', + items = [], + i = that.thead ? 0 : 1, + sortMethod = header.getAttribute('data-sort-method'), + sortOrder = header.getAttribute('aria-sort'); + + that.table.dispatchEvent(createEvent('beforeSort')); + + // If updating an existing sort, direction should remain unchanged. + if (!update) { + if (sortOrder === 'ascending') { + sortOrder = 'descending'; + } else if (sortOrder === 'descending') { + sortOrder = 'ascending'; + } else { + sortOrder = that.options.descending ? 'descending' : 'ascending'; + } + + header.setAttribute('aria-sort', sortOrder); + } + + if (that.table.rows.length < 2) return; + + // If we force a sort method, it is not necessary to check rows + if (!sortMethod) { + var cell; + while (items.length < 3 && i < that.table.tBodies[0].rows.length) { + if(columnKey) { + cell = getCellByKey(that.table.tBodies[0].rows[i].cells, columnKey); + } else { + cell = that.table.tBodies[0].rows[i].cells[column]; + } + + // Treat missing cells as empty cells + item = cell ? getInnerText(cell,that.options) : ""; + + item = item.trim(); + + if (item.length > 0) { + items.push(item); + } + + i++; + } + + if (!items) return; + } + + for (i = 0; i < sortOptions.length; i++) { + item = sortOptions[i]; + + if (sortMethod) { + if (item.name === sortMethod) { + sortFunction = item.sort; + break; + } + } else if (items.every(item.pattern)) { + sortFunction = item.sort; + break; + } + } + + that.col = column; + + for (i = 0; i < that.table.tBodies.length; i++) { + var newRows = [], + noSorts = {}, + j, + totalRows = 0, + noSortsSoFar = 0; + + if (that.table.tBodies[i].rows.length < 2) continue; + + for (j = 0; j < that.table.tBodies[i].rows.length; j++) { + var cell; + + item = that.table.tBodies[i].rows[j]; + if (item.getAttribute('data-sort-method') === 'none') { + // keep no-sorts in separate list to be able to insert + // them back at their original position later + noSorts[totalRows] = item; + } else { + if (columnKey) { + cell = getCellByKey(item.cells, columnKey); + } else { + cell = item.cells[that.col]; + } + // Save the index for stable sorting + newRows.push({ + tr: item, + td: cell ? getInnerText(cell,that.options) : '', + index: totalRows + }); + } + totalRows++; + } + // Before we append should we reverse the new array or not? + // If we reverse, the sort needs to be `anti-stable` so that + // the double negatives cancel out + if (sortOrder === 'descending') { + newRows.sort(stabilize(sortFunction, true)); + } else { + newRows.sort(stabilize(sortFunction, false)); + newRows.reverse(); + } + + // append rows that already exist rather than creating new ones + for (j = 0; j < totalRows; j++) { + if (noSorts[j]) { + // We have a no-sort row for this position, insert it here. + item = noSorts[j]; + noSortsSoFar++; + } else { + item = newRows[j - noSortsSoFar].tr; + } + + // appendChild(x) moves x if already present somewhere else in the DOM + that.table.tBodies[i].appendChild(item); + } + } + + that.table.dispatchEvent(createEvent('afterSort')); + }, + + refresh: function() { + if (this.current !== undefined) { + this.sortTable(this.current, true); + } + } + }; + + if (typeof module !== 'undefined' && module.exports) { + module.exports = Tablesort; + } else { + window.Tablesort = Tablesort; + } + })(); diff --git a/http/tablesort.number.min.js b/http/tablesort.number.min.js new file mode 100644 index 0000000..74963ac --- /dev/null +++ b/http/tablesort.number.min.js @@ -0,0 +1,6 @@ +/*! + * tablesort v5.2.1 (2021-10-30) + * http://tristen.ca/tablesort/demo/ + * Copyright (c) 2021 ; Licensed MIT +*/ +!function(){var a=function(a){return a.replace(/[^\-?0-9.]/g,"")},b=function(a,b){return a=parseFloat(a),b=parseFloat(b),a=isNaN(a)?0:a,b=isNaN(b)?0:b,a-b};Tablesort.extend("number",function(a){return a.match(/^[-+]?[£\x24Û¢´€]?\d+\s*([,\.]\d{0,2})/)||a.match(/^[-+]?\d+\s*([,\.]\d{0,2})?[£\x24Û¢´€]/)||a.match(/^[-+]?(\d)*-?([,\.]){0,1}-?(\d)+([E,e][\-+][\d]+)?%?$/)},function(c,d){return c=a(c),d=a(d),b(d,c)})}(); diff --git a/tool_CodeSigning.ps1 b/tool_CodeSigning.ps1 new file mode 100644 index 0000000..2ac8029 --- /dev/null +++ b/tool_CodeSigning.ps1 @@ -0,0 +1,52 @@ +<# + +$authenticode = New-SelfSignedCertificate -Subject "Haze Authenticode" -CertStoreLocation Cert:\LocalMachine\My -Type CodeSigningCert + +#1a +# Add the self-signed Authenticode certificate to the computer's root certificate store. +## Create an object to represent the LocalMachine\Root certificate store. + $rootStore = [System.Security.Cryptography.X509Certificates.X509Store]::new("Root","LocalMachine") +## Open the root certificate store for reading and writing. + $rootStore.Open("ReadWrite") +## Add the certificate stored in the $authenticode variable. + $rootStore.Add($authenticode) +## Close the root certificate store. + $rootStore.Close() + +#1b +$authenticode = Get-ChildItem Cert:\LocalMachine\Root | Where-Object {$_.Subject -eq "CN=Haze Authenticode"} +# Add the self-signed Authenticode certificate to the computer's trusted publishers certificate store. +## Create an object to represent the LocalMachine\TrustedPublisher certificate store. + $publisherStore = [System.Security.Cryptography.X509Certificates.X509Store]::new("TrustedPublisher","LocalMachine") +## Open the TrustedPublisher certificate store for reading and writing. + $publisherStore.Open("ReadWrite") +## Add the certificate stored in the $authenticode variable. + $publisherStore.Add($authenticode) +## Close the TrustedPublisher certificate store. + $publisherStore.Close() + + #2 + # Confirm if the self-signed Authenticode certificate exists in the computer's Personal certificate store + Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.Subject -eq "CN=Haze Authenticode"} +# Confirm if the self-signed Authenticode certificate exists in the computer's Root certificate store + Get-ChildItem Cert:\LocalMachine\Root | Where-Object {$_.Subject -eq "CN=Haze Authenticode"} +# Confirm if the self-signed Authenticode certificate exists in the computer's Trusted Publishers certificate store + Get-ChildItem Cert:\LocalMachine\TrustedPublisher | Where-Object {$_.Subject -eq "CN=Haze Authenticode"} +3#> + +cd $PSScriptRoot + + #3 + # Get the code-signing certificate from the local computer's certificate store with the name *ATA Authenticode* and store it to the $codeCertificate variable. +$codeCertificate = Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.Subject -eq "CN=Haze Authenticode"} + + + +# Sign the PowerShell script +# PARAMETERS: +# FilePath - Specifies the file path of the PowerShell script to sign, eg. C:\ATA\myscript.ps1. +# Certificate - Specifies the certificate to use when signing the script. +# TimeStampServer - Specifies the trusted timestamp server that adds a timestamp to your script's digital signature. Adding a timestamp ensures that your code will not expire when the signing certificate expires. +Set-AuthenticodeSignature -FilePath H:\src\_GitHub\fo-stats\FO_stats_v2.ps1 -Certificate $codeCertificate -TimeStampServer http://timestamp.digicert.com +Set-AuthenticodeSignature -FilePath H:\src\_GitHub\fo-stats\_FoDownloader.ps1 -Certificate $codeCertificate -TimeStampServer http://timestamp.digicert.com +Set-AuthenticodeSignature -FilePath H:\src\_GitHub\fo-stats\FO_stats_join-json.ps1 -Certificate $codeCertificate -TimeStampServer http://timestamp.digicert.com \ No newline at end of file diff --git a/tool_FindReplace.ps1 b/tool_FindReplace.ps1 new file mode 100644 index 0000000..ada4a21 --- /dev/null +++ b/tool_FindReplace.ps1 @@ -0,0 +1,47 @@ +$findReplace = @{} +#$files = gci .\2022_T\220531_SF-!\*.json +$files = gci '.\2022_T\_ALL\2022-05-01_10-38-53_`[turtler`]_blue_vs_red.json' + + +#Hashtable items to replace +$findReplace.'bentski' = 'bent' +$findReplace.'sobrahbent' = 'bent' +$findReplace.'sobrahzel' = 'zel' +$findReplace.'‹sobrah‹zel' = 'zel' +$findReplace.'‡s‡meht' = 'meht' +$findReplace.'sobrahmeht' = 'meht' +$findReplace.'\(1\)spas' = '!spas' +$findReplace.'"spas"' = '"!spas"' +$findReplace.'!hello' = '!pecan' +$findReplace.'‰b‰lagfox' = '‰b‰redfox' +$findReplace.'‡s‡meht' = 'meht' +$findReplace.'seabo' = 'seano' +$findReplace.'"shitpeas"' = '"seano"' +$findReplace.'(1)shitpeas' = 'seano' +$findReplace.'seanpeas' = 'seano' + +$findReplace.'"awm"' = '"‰b‰wm"' +$findReplace.'"loddy"' = '"!lordy"' +$findReplace.'"loddy"' = '"!lordy"' + +$files +'---' +foreach ($f in $files) { +$fn = $f -replace '\[','`[' -replace '\]','`]' + +$txt = gc -Path ($fn) -Raw + +foreach ($fr in $findReplace.Keys) { + $txt = $txt -replace $fr,$findReplace.$fr +} + +$txt | Out-File $fn + +} + + + + + + + diff --git a/tool_Join-Fix-2xJSON.ps1 b/tool_Join-Fix-2xJSON.ps1 new file mode 100644 index 0000000..2840247 --- /dev/null +++ b/tool_Join-Fix-2xJSON.ps1 @@ -0,0 +1,16 @@ +$strOut = '' + +foreach ($l in (gc '.\2022_T\220213_ZAF-BDE\unedited-swoop\2022-02-13_09-24-50_`[ff-swoop`]_blue_vs_red.json')) { + +if ($l -match '"playerTeam": ([1-2])') { $l = $l -replace """playerTeam"": [1-2]","""playerTeam"": $(Switch ($matches[1]) { 1 { '2' }; 2 { '1' } })" } +if ($l -match '"targetTeam": ([1-2])') { $l = $l -replace """targetTeam"": [1-2]","""targetTeam"": $(Switch ($matches[1]) { 1 { '2' }; 2 { '1' } })" } +if ($l -match '"attackerTeam": ([1-2])') { $l = $l -replace """attackerTeam"": [1-2]","""attackerTeam"": $(Switch ($matches[1]) { 1 { '2' }; 2 { '1' } })" } +if ($l -match '"time": ([0-9]{1,4})') { $l = $l -replace """time"": $($matches[1])","""time"": $([int]$matches[1] + 600)" } + +$strOut += "$l`n" + +} + + +$strOut | Out-File .\new.txt +& .\new.txt \ No newline at end of file