aboutsummaryrefslogtreecommitdiff
path: root/backend/parser/parser.go
diff options
context:
space:
mode:
Diffstat (limited to 'backend/parser/parser.go')
-rw-r--r--backend/parser/parser.go196
1 files changed, 180 insertions, 16 deletions
diff --git a/backend/parser/parser.go b/backend/parser/parser.go
index 1a80d4a..4605600 100644
--- a/backend/parser/parser.go
+++ b/backend/parser/parser.go
@@ -4,30 +4,52 @@ import (
4 "errors" 4 "errors"
5 "math" 5 "math"
6 "os" 6 "os"
7 "regexp"
8 "strconv"
9 "strings"
7 10
8 "github.com/pektezol/bitreader" 11 "github.com/pektezol/bitreader"
9) 12)
10 13
14type Result struct {
15 MapID int
16 ServerNumber int
17 PortalCount int
18 TickCount int
19 HostSteamID string
20 PartnerSteamID string
21 IsHost bool
22}
23
11// Don't try to understand it, feel it. 24// Don't try to understand it, feel it.
12func ProcessDemo(filePath string) (portalCount int, tickCount int, hostSteamID string, partnerSteamID string, err error) { 25func ProcessDemo(filePath string) (Result, error) {
26 var result Result
13 file, err := os.Open(filePath) 27 file, err := os.Open(filePath)
14 if err != nil { 28 if err != nil {
15 return 0, 0, "", "", err 29 return Result{}, err
16 } 30 }
17 reader := bitreader.NewReader(file, true) 31 reader := bitreader.NewReader(file, true)
18 demoFileStamp := reader.TryReadString() 32 demoFileStamp := reader.TryReadString()
19 demoProtocol := reader.TryReadSInt32() 33 demoProtocol := reader.TryReadSInt32()
20 networkProtocol := reader.TryReadSInt32() 34 networkProtocol := reader.TryReadSInt32()
21 reader.SkipBytes(1056) 35 serverName := reader.TryReadStringLength(260)
36 // clientName := reader.TryReadStringLength(260)
37 reader.SkipBytes(260)
38 mapName := reader.TryReadStringLength(260)
39 reader.SkipBytes(276)
22 if demoFileStamp != "HL2DEMO" { 40 if demoFileStamp != "HL2DEMO" {
23 return 0, 0, "", "", errors.New("invalid demo file stamp") 41 return Result{}, errors.New("invalid demo file stamp")
24 } 42 }
25 if demoProtocol != 4 { 43 if demoProtocol != 4 {
26 return 0, 0, "", "", errors.New("this parser only supports demos from new engine") 44 return Result{}, errors.New("this parser only supports demos from new engine")
27 } 45 }
28 if networkProtocol != 2001 { 46 if networkProtocol != 2001 {
29 return 0, 0, "", "", errors.New("this parser only supports demos from portal 2") 47 return Result{}, errors.New("this parser only supports demos from portal 2")
48 }
49 if mapDict[mapName] == 0 {
50 return Result{}, errors.New("demo recorded on an invalid map")
30 } 51 }
52 result.MapID = mapDict[mapName]
31 for { 53 for {
32 packetType := reader.TryReadUInt8() 54 packetType := reader.TryReadUInt8()
33 reader.SkipBits(40) 55 reader.SkipBits(40)
@@ -119,7 +141,17 @@ func ProcessDemo(filePath string) (portalCount int, tickCount int, hostSteamID s
119 packetReader.SkipBytes(2) 141 packetReader.SkipBytes(2)
120 packetReader.SkipBits(uint64(packetReader.TryReadUInt16())) 142 packetReader.SkipBits(uint64(packetReader.TryReadUInt16()))
121 case 16: 143 case 16:
122 packetReader.TryReadString() 144 print := packetReader.TryReadString()
145 re := regexp.MustCompile(`Server Number: (\d+)`)
146 match := re.FindStringSubmatch(print)
147 if len(match) >= 1 {
148 serverNumber := match[1]
149 n, err := strconv.Atoi(serverNumber)
150 if err != nil {
151 return Result{}, err
152 }
153 result.ServerNumber = n
154 }
123 case 17: 155 case 17:
124 var length uint16 156 var length uint16
125 if packetReader.TryReadBool() { 157 if packetReader.TryReadBool() {
@@ -180,8 +212,8 @@ func ProcessDemo(filePath string) (portalCount int, tickCount int, hostSteamID s
180 NumPortals: userMessageReader.TryReadSInt32(), 212 NumPortals: userMessageReader.TryReadSInt32(),
181 TimeTaken: userMessageReader.TryReadSInt32(), 213 TimeTaken: userMessageReader.TryReadSInt32(),
182 } 214 }
183 portalCount = int(scoreboardTempUpdate.NumPortals) 215 result.PortalCount = int(scoreboardTempUpdate.NumPortals)
184 tickCount = int(math.Round(float64((float32(scoreboardTempUpdate.TimeTaken) / 100.0) / float32(1.0/60.0)))) 216 result.TickCount = int(math.Round(float64((float32(scoreboardTempUpdate.TimeTaken) / 100.0) / float32(1.0/60.0))))
185 } 217 }
186 case 24: 218 case 24:
187 packetReader.SkipBits(20) 219 packetReader.SkipBits(20)
@@ -216,8 +248,8 @@ func ProcessDemo(filePath string) (portalCount int, tickCount int, hostSteamID s
216 packetReader.SkipBytes(packetReader.TryReadBits(32)) 248 packetReader.SkipBytes(packetReader.TryReadBits(32))
217 case 33: 249 case 33:
218 packetReader.SkipBits(packetReader.TryReadBits(32)) 250 packetReader.SkipBits(packetReader.TryReadBits(32))
219 default: 251 // default:
220 return 0, 0, "", "", errors.New("unknown msg type") 252 // return Result{}, errors.New(fmt.Sprintf("unknown msg type %d", messageType))
221 } 253 }
222 } 254 }
223 case 3, 7: 255 case 3, 7:
@@ -252,7 +284,7 @@ func ProcessDemo(filePath string) (portalCount int, tickCount int, hostSteamID s
252 if stringTableReader.TryReadBool() { 284 if stringTableReader.TryReadBool() {
253 byteLen, err := stringTableReader.ReadBits(16) 285 byteLen, err := stringTableReader.ReadBits(16)
254 if err != nil { 286 if err != nil {
255 return 0, 0, "", "", errors.New("error on reading entry length") 287 return Result{}, errors.New("error on reading entry length")
256 } 288 }
257 stringTableEntryReader := bitreader.NewReaderFromBytes(stringTableReader.TryReadBytesToSlice(byteLen), true) 289 stringTableEntryReader := bitreader.NewReaderFromBytes(stringTableReader.TryReadBytesToSlice(byteLen), true)
258 if tableName == "userinfo" { 290 if tableName == "userinfo" {
@@ -285,10 +317,14 @@ func ProcessDemo(filePath string) (portalCount int, tickCount int, hostSteamID s
285 userInfo.FilesDownloaded = stringTableEntryReader.TryReadUInt8() 317 userInfo.FilesDownloaded = stringTableEntryReader.TryReadUInt8()
286 stringTableEntryReader.SkipBytes(3) 318 stringTableEntryReader.SkipBytes(3)
287 if guidCount == 0 { 319 if guidCount == 0 {
288 hostSteamID = userInfo.GUID 320 result.HostSteamID = userInfo.GUID
321 if strings.Contains(serverName, "localhost") {
322 result.IsHost = true
323 }
289 } else if guidCount == 1 { 324 } else if guidCount == 1 {
290 partnerSteamID = userInfo.GUID 325 result.PartnerSteamID = userInfo.GUID
291 } 326 }
327 guidCount++
292 } 328 }
293 } 329 }
294 } 330 }
@@ -303,11 +339,139 @@ func ProcessDemo(filePath string) (portalCount int, tickCount int, hostSteamID s
303 } 339 }
304 } 340 }
305 default: 341 default:
306 return 0, 0, "", "", errors.New("invalid packet type") 342 return Result{}, errors.New("invalid packet type")
307 } 343 }
308 if packetType == 7 { 344 if packetType == 7 {
309 break 345 break
310 } 346 }
311 } 347 }
312 return portalCount, tickCount, hostSteamID, partnerSteamID, nil 348 return result, nil
349}
350
351var mapDict = map[string]int{
352 "sp_a1_intro1": 1,
353 "sp_a1_intro2": 2,
354 "sp_a1_intro3": 3,
355 "sp_a1_intro4": 4,
356 "sp_a1_intro5": 5,
357 "sp_a1_intro6": 6,
358 "sp_a1_intro7": 7,
359 "sp_a1_wakeup": 8,
360 "sp_a2_intro": 9,
361
362 "sp_a2_laser_intro": 10,
363 "sp_a2_laser_stairs": 11,
364 "sp_a2_dual_lasers": 12,
365 "sp_a2_laser_over_goo": 13,
366 "sp_a2_catapult_intro": 14,
367 "sp_a2_trust_fling": 15,
368 "sp_a2_pit_flings": 16,
369 "sp_a2_fizzler_intro": 17,
370
371 "sp_a2_sphere_peek": 18,
372 "sp_a2_ricochet": 19,
373 "sp_a2_bridge_intro": 20,
374 "sp_a2_bridge_the_gap": 21,
375 "sp_a2_turret_intro": 22,
376 "sp_a2_laser_relays": 23,
377 "sp_a2_turret_blocker": 24,
378 "sp_a2_laser_vs_turret": 25,
379 "sp_a2_pull_the_rug": 26,
380
381 "sp_a2_column_blocker": 27,
382 "sp_a2_laser_chaining": 28,
383 "sp_a2_triple_laser": 29,
384 "sp_a2_bts1": 30,
385 "sp_a2_bts2": 31,
386
387 "sp_a2_bts3": 32,
388 "sp_a2_bts4": 33,
389 "sp_a2_bts5": 34,
390 "sp_a2_core": 35,
391
392 "sp_a3_01": 36,
393 "sp_a3_03": 37,
394 "sp_a3_jump_intro": 38,
395 "sp_a3_bomb_flings": 39,
396 "sp_a3_crazy_box": 40,
397 "sp_a3_transition01": 41,
398
399 "sp_a3_speed_ramp": 42,
400 "sp_a3_speed_flings": 43,
401 "sp_a3_portal_intro": 44,
402 "sp_a3_end": 45,
403
404 "sp_a4_intro": 46,
405 "sp_a4_tb_intro": 47,
406 "sp_a4_tb_trust_drop": 48,
407 "sp_a4_tb_wall_button": 49,
408 "sp_a4_tb_polarity": 50,
409 "sp_a4_tb_catch": 51,
410 "sp_a4_stop_the_box": 52,
411 "sp_a4_laser_catapult": 53,
412 "sp_a4_laser_platform": 54,
413 "sp_a4_speed_catch": 55,
414 "sp_a4_jump_polarity": 56,
415
416 "sp_a4_finale1": 57,
417 "sp_a4_finale2": 58,
418 "sp_a4_finale3": 59,
419 "sp_a4_finale4": 60,
420
421 "mp_coop_start": 61,
422 "mp_coop_lobby_2": 62,
423
424 "mp_coop_doors": 63,
425 "mp_coop_race_2": 64,
426 "mp_coop_laser_2": 65,
427 "mp_coop_rat_maze": 66,
428 "mp_coop_laser_crusher": 67,
429 "mp_coop_teambts": 68,
430
431 "mp_coop_fling_3": 69,
432 "mp_coop_infinifling_train": 70,
433 "mp_coop_come_along": 71,
434 "mp_coop_fling_1": 72,
435 "mp_coop_catapult_1": 73,
436 "mp_coop_multifling_1": 74,
437 "mp_coop_fling_crushers": 75,
438 "mp_coop_fan": 76,
439
440 "mp_coop_wall_intro": 77,
441 "mp_coop_wall_2": 78,
442 "mp_coop_catapult_wall_intro": 79,
443 "mp_coop_wall_block": 80,
444 "mp_coop_catapult_2": 81,
445 "mp_coop_turret_walls": 82,
446 "mp_coop_turret_ball": 83,
447 "mp_coop_wall_5": 84,
448
449 "mp_coop_tbeam_redirect": 85,
450 "mp_coop_tbeam_drill": 86,
451 "mp_coop_tbeam_catch_grind_1": 87,
452 "mp_coop_tbeam_laser_1": 88,
453 "mp_coop_tbeam_polarity": 89,
454 "mp_coop_tbeam_polarity2": 90,
455 "mp_coop_tbeam_polarity3": 91,
456 "mp_coop_tbeam_maze": 92,
457 "mp_coop_tbeam_end": 93,
458
459 "mp_coop_paint_come_along": 94,
460 "mp_coop_paint_redirect": 95,
461 "mp_coop_paint_bridge": 96,
462 "mp_coop_paint_walljumps": 97,
463 "mp_coop_paint_speed_flings": 98,
464 "mp_coop_paint_red_racer": 99,
465 "mp_coop_paint_speed_catch": 100,
466 "mp_coop_paint_longjump_intro": 101,
467
468 "mp_coop_seperation_1": 102,
469 "mp_coop_tripleaxis": 103,
470 "mp_coop_catapult_catch": 104,
471 "mp_coop_2paints_1bridge": 105,
472 "mp_coop_paint_conversion": 106,
473 "mp_coop_bridge_catch": 107,
474 "mp_coop_laser_tbeam": 108,
475 "mp_coop_paint_rat_maze": 109,
476 "mp_coop_paint_crazy_box": 110,
313} 477}