bananahampster / hampalyzer Goto Github PK
View Code? Open in Web Editor NEWA Half-Life GoldSrc log parser, targeted toward TFC.
Home Page: http://app.hampalyzer.com
License: GNU General Public License v3.0
A Half-Life GoldSrc log parser, targeted toward TFC.
Home Page: http://app.hampalyzer.com
License: GNU General Public License v3.0
For games where HLTV was recorded it would be awesome to be able to associate the HLTV demo with the match logs.
Adding file hosting to hampalyzer doesn't seem practical, but perhaps we could have a comment field where people could post the links to the HLTVs - or say "gg" or congratulate Nomad on getting airshot so many times in 1 round, etc.
It looks like there may be multiple issues with this - with the following line in place:
L 05/03/2022 - 12:44:31: "hello? Nomad [BOISY] pls<5><STEAM_0:1:5107>" committed suicide with "door_rotating" (world)
the result is "There was an error parsing the logs. More info: «no additional info»"
If I manually change that suicide from "door_rotating" to "worldspawn" instead, the result is a 403 error: http://app.hampalyzer.com/parsedlogs/Unknown-2022-May-3-12-36-t46aa/
Currently there's a Search field at the top of the screen when viewing an individual log, but it doesn't seem to do anything :)
It would be cool to be able to find parsed logs by searching for things like...
What I'd really love to be able to do is search for games where [player] played [class] on [map] but that may be too greedy :p
Attempting to process the log files from the most recent OST pickup night failed with the error: There was an error parsing the logs. More info: «no additional info
Log files:
OST-Pickup-2022-01-02-Rd1-Troy2.log
OST-Pickup-2022-01-02-Rd2-Palermo.log
OST-Pickup-2022-01-02-Rd3-cmhf.log
OST-Pickup-2022-01-02-Rd4-Rasen.log
OST-Pickup-2022-01-02-Rd5-Rasen.log
»
L0616058.log
L0616057.log
Attempting to submit these logs results in an error:
There was an error parsing the logs. More info: «no additional info»
Determine on parse if a set of logs is valid (e.g., more than 50% of the player pool overlaps, same map on both logs, nearly same time on both logs, identical logs already exist in DB [same server, players, map, play time]).
If so, error and return HTTP 400 with descriptive error message.
Add a new parameter to force-parse logs anyway.
Augment logs
DB with the following column: is_valid boolean DEFAULT FALSE
to support saving this state in the database.
Should probably use PostGIS (see ST_Within and 3D functions).
For any event that contains xyz coordinates, check the DB if the map exists and do a spatial query to find the named region. Add the named region to the extraData
field of the event.
Insert spatial 3D regions into a database like the following:
MAPLOCATIONS table (table is new)
name | type | values | notes |
---|---|---|---|
locationId | auto-increment, NOT NULL | ||
map | string | NOT NULL, add index | |
name | string | user-provided name/callout | |
geom | geometry | will be POLYGON Z, typ. |
SELECT name
FROM mapLocations
WHERE ST_3DIntersects(
geom,
'POINT Z($x, $y, $z)'::geometry
)
LIMIT 1;
This requires updating the debian version of the hampalyzer VM to bullseye to update apt packages to include the PostGIS extension. This is probably good housekeeping anyway...
Currently flag returns appear to be tracked based on the team name indicated in the message that's triggered on the return, for example:
case "Blue Flag Returned Message":
case "Red Flag Returned Message":
eventType = EventType.FlagReturn;
data.team = Event.parseTeam(nonPlayerDataParts[2].split(" ")[0]);
break;
However, in some (ADL) maps this information may not be present, for example this is what triggers on Magelli:
L 08/10/2022 - 04:47:12: World triggered "Flag has returned Info"
L 08/10/2022 - 04:47:12: World triggered "#dustbowl_flag_returned"
Magelli may be an edge case due to the number of helper entities used in the map, most of which have custom names, but I believe a similar issue affects other one-flag type maps as well. I am happy to add the various customized versions of these flag return messages as they come up, but currently the attempt to parse the team based on the message content results in an error. For example, using the Magelli trigger above:
Failed to parse line number 603: World triggered "Flag has returned Info"
file:///[...]/hampalyzer/dist/parser.js:827
throw error;
^
Waiting for the debugger to disconnect...
unknown team: flag
Blarghalyzer has a feature which breaks down kills by weapon used, which I enjoy referencing for at-a-glance to see how truly useless my shotgun is.
It would really be awesome to see total damage dealt (not just kills) but I believe that requires a plugin?
Be interesting discussion point after matches, especially if players DON'T know how MVP is calculated. A fair weighted MVP_score formula would be:
MVP_score() = 0.7(kills) + 2.8(sg_kills) + 1.4(touches) + 2.2(touches_initial) - (team_kills) + 5(caps_bonus [coast to coast caps])
The formula equally weights offense and defense performance, while rewarding plays that benefit the team and contribute to winning the most, without solely focusing on kills.
The player with the highest MVP score at the end of the match is named MVP, with one exception:
if {{name}} == "doug" OR "Doug" {
MVP_score == MVP_score / 2
}
For Doug's MVP score only.
Troy2 is an ADL map with various custom entity names which cause lots of parse errors.
For example, see:
http://app.hampalyzer.com/parsedlogs/OldSchool-2022-Aug-28-04-36/
and
http://app.hampalyzer.com/parsedlogs/OldSchool-2022-Aug-28-04-54/
Log files for reference:
troy2-R1-L0828012.log
troy2-R2-L0828014.log
I haven't seen any particular pattern yet, but sometimes (randomly?) the classes played are incorrect. This is most commonly noticed when someone plays scout and medic, but only scout is shown. Here's an example: http://app.hampalyzer.com/parsedlogs/Coach's-2021-Jul-15-15-01/ (I did not get 22 kills as scout :))
Logs courtesy of coachsouz:
R1: https://pastebin.com/nAGbSBM3
R2 (in case it's relevant): https://pastebin.com/vB7jdGKB
I seem to be getting 404 errors fairly regularly for the last week - here's one example:
L0517050.log
L0517051.log
http://app.hampalyzer.com/parsedlogs/hello?-2022-May-17-12-33
(not sure if this is useful but blarghs seem to work fine for comparison:
http://www.blarghalyzer.com/parsedlogs/l0517050/
http://www.blarghalyzer.com/parsedlogs/l0517051/ )
With the completion of #52, ignore marked invalid logs unless an override parameter is given to the server process.
This may be redundant depending on the resolution of #26 but if I had a particularly good game or got an unlikely MVP or something similarly brag-worthy, it might be neat if I could flag particular logs for myself and be able to view my list of favorite/saved logs somehow.
http://inhouse.site.nfoservers.com/akw/L1004011-2021/
http://app.hampalyzer.com/parsedlogs/Unknown-2021-Oct-4-01-11/
mrp had 0:23 in hampalyzer, 0:02 in actual. similar issue with initials: hamp says 4, actual says 6.
rasen-R1-L0807027.log
rasen-R2-L0807031.log
It looks like Rasen uses slightly unusual team names:
"#Dustbowl_team1" and "#Dustbowl_team2"
dustbowl_team1 / dustbowl_team2 are already supported, but these have a leading '#' sign.
Not sure if the ideal solution is to trim off the # or just add a new case statement to catch this, but I only know how to do one of those things so I'll submit a PR shortly :p
In most maps the number of times each player touches the flag is calculated and shown correctly. On proton_l this calculation (or the trigger?) seems to be broken.
See http://app.hampalyzer.com/parsedlogs/Coach%27s-2021-Jul-14-18-13 (coachsouz in https://discord.gg/2AuUCZYnKv may be able to retrieve actual logs if needed).
Edit: coach apparently saw this and provided the full logs already, here's some pastebin links since it looks like I can't attach them directly:
Round 1: https://pastebin.com/f88SCzCH
Round 2: https://pastebin.com/5xH7Gmys
<3
I don't think this is reproducible since I'm not sure what happened, but this log came out all garbled: http://app.hampalyzer.com/parsedlogs/Coach's-2022-Mar-16-23-17/
The equivalent blarghs seem to be fine:
http://blarghalyzer.com/parsedlogs/coach-l0316055/
http://blarghalyzer.com/parsedlogs/coach-l0316056/
Raw logs:
L0316053.log
L0316054.log
Add DB insertion of individual events: change DB structure and insert into DB on parse.
On reparse, truncate event
table and restart eventId increment.
Use the following DB schema;
EVENT table (table is new)
name | type | values | notes |
---|---|---|---|
eventId | auto-increment, NOT NULL | ||
logId | log table id ref, NOT NULL | ||
isFirstLog | bool | default true | |
eventType | enum | 0-71 | NOT NULL |
rawLine | string | NOT NULL | |
lineNumber | number | NOT NULL | |
timestamp | datetime | NOT NULL | |
gameTime | number | seconds | NOT NULL |
extraData | string | (prefer json format?) | |
playerFrom | player table id ref | ||
playerFromClass | short | 0-9 | |
playerTo | player table id ref | ||
playerToClass | short | 0-9 | |
withWeapon | short | 0-39 | |
playerFromFlag | bool | default false | |
playerToFlag | bool | default false |
PLAYER table (table is new)
name | type | desc | notes |
---|---|---|---|
playerId | auto-increment, NOT NULL | ||
playerName | string | NOT NULL | |
playerAlias | string | ||
steamId | number | see SteamID doc |
LOGS table (* are new columns)
name | type | values | notes |
---|---|---|---|
* logId | auto-increment, NOT NULL | ||
parsedlog | string | output URI slug | |
log_file1 | string | matches name in uploads/ | |
log_file2 | string | matches name in uploads/ | |
date_parsed | datetime | initial upload time | |
date_match | datetime | reported in local time | |
map | string | can be "" | |
server | string | ||
num_players | int | ||
* is_valid | bool | default false |
PARSEDLOGS table (table is new)
name | type | values | notes |
---|---|---|---|
logId | auto-increment, NOT NULL | ||
jsonSummary | varchar(MAX) | full json |
http://app.hampalyzer.com/parsedlogs/OldSchool-2022-Aug-10-04-36/ shows no flag captures by anyone on either team.
For comparison, blarghalyzer reports various captures from players on each team: https://www.blarghalyzer.com/parsedlogs/magelli-l0810014
Relevant log file: magelli-L0810014.log
Baconbowl's cap 3 poses an interesting challenge because it doesn't appear to have any player trigger, just a world trigger.
The i_t_g for both cap 1 and cap 2 have "netname" "Capture Point 1"
which is weird but at least it shows up as the appropriate player trigger in the logs, for example:
L 08/30/2022 - 10:32:28: "hello? Nomad [BOISY] pls<3><STEAM_0:1:5107><#Dustbowl_team1>" triggered "Capture Point 1"
Cap 1 and cap 2 also have b_b
values which are logged as world triggers, for example:
L 08/30/2022 - 10:32:28: World triggered "#dustbowl_blue_secures_two"
However, the cap 3 i_t_g does not have a netname
and doesn't log any player triggers.
Cap 3 has "target" "cap3"
which is a multi_manager, which has... "win_message" "1"
The info_tfgoal with "targetname" "win_message"
has "b_b" "#dustbowl_blue_caps"
which is the only thing we see in the log when someone captures point 3, for example:
L 08/30/2022 - 10:34:50: World triggered "#dustbowl_blue_caps"
It's possible I am just dumb, but it seems to me like this problem shouldn't exist if the map entities were fixed, and maybe an entity edit is the ideal solution.
With that said, is it possible to solve this problem in hampalyzer?
Is there a better way than waiting for that #dustbowl_blue_caps"
world trigger, then looking back at who triggered "Flag 3"
most recently?
Example games:
http://app.hampalyzer.com/parsedlogs/OldSchool-2022-Aug-27-06-24/ (no cap 3 because red successfully defended)
http://app.hampalyzer.com/parsedlogs/OldSchool-2022-Aug-27-06-51/ (Suicide_Kid captures point 3 but the only indication of this in the log is World triggered "#dustbowl_blue_caps"
)
Example log files:
baconbowl_r-R1-L0827017.log
baconbowl_r-R2-L0827019.log
L0830046.log (log from me finishing the map by myself in an empty server)
Entity definitions:
baconbowl_r.ent.txt
Instead of statically rendering logs, consider rendering via JSON blob from DB. Requires implementation of a template outside of handlebars.
Arendal is an ADL map with various custom entity names which cause parse errors.
For example, see:
http://app.hampalyzer.com/parsedlogs/OldSchool-2022-Aug-28-05-28/
and
http://app.hampalyzer.com/parsedlogs/OldSchool-2022-Aug-28-05-47/
Log files for reference:
arendal_l_rc3-R1-L0828017.log
arendal_l_rc3-R2-L0828019.log
Possibly related to #25?
If I try to upload these logs I get a 403 error: http://app.hampalyzer.com/parsedlogs/Coach's-2022-Mar-17-20-44/
Blarghalyzer version seems okay:
http://blarghalyzer.com/parsedlogs/coach-l0317043/
http://blarghalyzer.com/parsedlogs/coach-l0317044/
Raw logs attached.
L0317036.log
L0317037.log
Edit: NEVERMIND I AM DUMB SEE BELOW.
Attempting to submit these logs results in an error:
There was an error parsing the logs. More info: «no additional info»
Hamp sent me the error (healing a tranq effect) before I finished submitting this ticket but I'll post it here for the record:
Failed to parse line number 3791: "Features-<4><STEAM_0:0:92298>" triggered "Medic_Cured_Tranquilisation" against "Doug<8><STEAM_0:1:5622>"
(removing this line allowed the logs to be uploaded and parsed successfully)
Blargh has a feature which shows whether a kill was on a flag carrier or not, which is sometimes interesting to see.
For example:
http://blarghalyzer.com/parsedlogs/dump-l0327157/Player_0_1_5107.html
vs
http://app.hampalyzer.com/parsedlogs/Garbage-2022-Mar-27-21-53/p5107.html
http://app.hampalyzer.com/parsedlogs/Inhouse-2021-Nov-17-22-21/ -- hamp shows as medic on D even though he was demo the whole rd
Currently, while per-player data for each team appears to be calculated correctly, the Total
calculations for some fields are only done for one team, not the other. This works fine for OvD CTF games, but for ADL games it would make sense to sum all available fields.
I think this is the relevant code from templateUtils.ts
:
// offense summary
Handlebars.registerHelper('offenseSummary', function(this: OffenseTeamStats, damageStatsExist: boolean, teamId: string, players?: OutputPlayer[]) {
const isComparison = teamId === 'Comp'; // first parameter can sometimes be object??
return `
<tr ${isComparison && 'class="comp"'}>
<td class="team">${TemplateUtils.getTeamName(teamId, players)}</td>
${TemplateUtils.getRow(this.frags, isComparison, "kills-total")}
${TemplateUtils.getRow(this.kills, isComparison, "kills")}
${TemplateUtils.getRow(this.team_kills, isComparison, "team-kills")}
${TemplateUtils.getRow(this.conc_kills, isComparison, "conc-kills")}
${TemplateUtils.getRow(this.sg_kills, isComparison, "sentry-kills")}` +
(damageStatsExist ? `
${TemplateUtils.getRow(this.damage_enemy, isComparison, "damage-enemy")}
${TemplateUtils.getRow(this.damage_team, isComparison, "damage-team")}` : "") + `
${TemplateUtils.getRow(this.deaths, isComparison, "deaths-total")}
${TemplateUtils.getRow(this.d_enemy, isComparison, "deaths")}
${TemplateUtils.getRow(this.d_self, isComparison, "suicides")}
${TemplateUtils.getRow(this.d_team, isComparison, "team-deaths")}
${TemplateUtils.getRow(this.concs, isComparison, "concs")}
${TemplateUtils.getRow(this.caps, isComparison, "flag-captures")}
${TemplateUtils.getRow(this.touches, isComparison, "flag-touches")}
${TemplateUtils.getRow(this.toss_percent, isComparison, "flag-toss-percentage")}
${TemplateUtils.getRow(this.flag_time, isComparison, "flag-time")}
</tr>`;
});
Handlebars.registerHelper('defenseSummary', function(this: DefenseTeamStats, damageStatsExist: boolean, teamId: string, players?: OutputPlayer[]) {
const isComparison = teamId === 'Comp'; // first parameter can sometimes be object??
return `
<tr ${isComparison && 'class="comp"'}>
<td class="team">${TemplateUtils.getTeamName(teamId, players)}</td>
${TemplateUtils.getRow(this.frags, isComparison, "kills-total")}
${TemplateUtils.getRow(this.kills, isComparison, "kills")}
${TemplateUtils.getRow(this.team_kills, isComparison, "team-kills")}
${TemplateUtils.getRow(this.conc_kills, isComparison, "conc-kills")}` +
(damageStatsExist ? `
${TemplateUtils.getRow(this.damage_enemy, isComparison, "damage-enemy")}
${TemplateUtils.getRow(this.damage_team, isComparison, "damage-team")}` : "") + `
${TemplateUtils.getRow(this.deaths, isComparison, "deaths-total")}
${TemplateUtils.getRow(this.d_enemy, isComparison, "deaths")}
${TemplateUtils.getRow(this.d_self, isComparison, "suicides")}
${TemplateUtils.getRow(this.d_team, isComparison, "team-deaths")}
${TemplateUtils.getRow(this.airshots, isComparison, "airshots")}
</tr>`;
});
Several data points are totalled for blue but not for red, but looks like the only data that's unique to red team is ${TemplateUtils.getRow(this.airshots, isComparison, "airshots")}
Can these summations be merged so that no matter what team plays offense or defense, the data totals are counted?
Sorry this is super vague, but sometimes, some logs can't be parsed. Doesn't seem to be a pattern based on server, map, or any other obvious thing that jumped out at me, but occasionally logs will give this error instead of parsing.
Attached are some logs that errored for me.
schtopr1.log
schtopr2.log
Blarghalyzer has a feature which allows viewing of mm1 and mm2 chat - there may be an argument for hiding mm2 but being able to see mm1 messages (and timestamps) is super handy for finding the moment a thing happened that made everyone stop and type about it. :)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.